Skip to content

Fix Wrong Exception on Cli run when Command Not found #40063

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 2.4-develop
Choose a base branch
from

Conversation

Genaker
Copy link
Contributor

@Genaker Genaker commented Jul 10, 2025

Before Fix error on Cli run:

image

After :

image

Description (*)

If you have ever tried to run bin/magento and been greeted with the maddening: "There is no command found in the namespace"

you already know how opaque Magento (Adobe Commerce) can be when a single CLI command class is broken. One constructor typo, a missing dependency, or a bad module upgrade and—boom—the whole console goes dark. No stack-trace, no filename, no clue. You are left spelunking through logs or disabling modules by trial and error. More than 10 years Adobe is not able to fix this critical issue! I'm tired and submitted the PR.

What My PR tiny but vital quality-of-life improvement:

  • Catches the original exception thrown while Magento is assembling its command list.

  • Prints the real message and class name before the fallback “no command found” notice.

  • The change is only a few lines, but the impact is huge: you immediately see which command class failed and why, so you can fix the root cause in minutes instead of hours.

Why every extension author & integrator should care:

  • Faster CI/CD – Broken modules fail loudly during automated builds; no more silent time-outs.

  • Happier merchants – Support teams pinpoint faulty third-party code instead of asking store owners for full backups.

  • Lean core – The patch touches only CLI bootstrap code; zero risk to storefront performance.

Thanks for backing real-world developer experience improvements—your click today could save countless debugging hundreds hours tomorrow!

Fixes #40006

Related Issue

Copy link

m2-assistant bot commented Jul 10, 2025

Hi @Genaker. Thank you for your contribution!
Here are some useful tips on how you can test your changes using Magento test environment.
❗ Automated tests can be triggered manually with an appropriate comment:

  • @magento run all tests - run or re-run all required tests against the PR changes
  • @magento run <test-build(s)> - run or re-run specific test build(s)
    For example: @magento run Unit Tests

<test-build(s)> is a comma-separated list of build names.

Allowed build names are:
  1. Database Compare
  2. Functional Tests CE
  3. Functional Tests EE
  4. Functional Tests B2B
  5. Integration Tests
  6. Magento Health Index
  7. Sample Data Tests CE
  8. Sample Data Tests EE
  9. Sample Data Tests B2B
  10. Static Tests
  11. Unit Tests
  12. WebAPI Tests
  13. Semantic Version Checker

You can find more information about the builds here
ℹ️ Run only required test builds during development. Run all test builds before sending your pull request for review.


For more details, review the Code Contributions documentation.
Join Magento Community Engineering Slack and ask your questions in #github channel.

@ihor-sviziev
Copy link
Contributor

Just wonder if we can do a bit different approach - throw different type of exception when the command not found, and handle it differently. What do you think?

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR improves error handling in the Magento CLI system when commands fail to initialize, providing developers with more detailed exception information instead of the generic "no commands defined in namespace" error. The change enhances debugging experience by showing the actual root cause when command classes are broken due to missing dependencies, constructor errors, or other issues.

  • Adds exception tracking for command initialization failures
  • Implements clearer error messaging that shows the original exception details
  • Moves setup command initialization to occur before other commands for better error isolation

Comment on lines +66 to +67
* GetCommands exception.
*
Copy link
Preview

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docblock comment 'GetCommands exception' should be more descriptive. Consider: 'Exception thrown during command list initialization.'

Suggested change
* GetCommands exception.
*
* Exception thrown during command list initialization.
*

Copilot uses AI. Check for mistakes.

Comment on lines +147 to +148
$combinedErrorMessage = "Exception during console commands initialization: " .
$this->getCommandsException->getMessage() . PHP_EOL;
Copy link
Preview

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The string concatenation spans multiple lines unnecessarily. Consider combining into a single line or using sprintf for better readability: sprintf('Exception during console commands initialization: %s%s', $this->getCommandsException->getMessage(), PHP_EOL)

Suggested change
$combinedErrorMessage = "Exception during console commands initialization: " .
$this->getCommandsException->getMessage() . PHP_EOL;
$combinedErrorMessage = sprintf(
'Exception during console commands initialization: %s%s',
$this->getCommandsException->getMessage(),
PHP_EOL
);

Copilot uses AI. Check for mistakes.

Comment on lines +181 to 186
if (class_exists(\Magento\Setup\Console\CommandList::class)) {
$setupCommandList = new \Magento\Setup\Console\CommandList($this->serviceManager);
$commands = array_merge($commands, $setupCommandList->getCommands());
}

if ($this->objectManager->get(DeploymentConfig::class)->isAvailable()) {
Copy link
Preview

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setup command initialization has been moved outside the deployment config check, which changes the execution order. This should be documented or the reasoning should be clearer, as it may affect command availability in different deployment states.

Suggested change
if (class_exists(\Magento\Setup\Console\CommandList::class)) {
$setupCommandList = new \Magento\Setup\Console\CommandList($this->serviceManager);
$commands = array_merge($commands, $setupCommandList->getCommands());
}
if ($this->objectManager->get(DeploymentConfig::class)->isAvailable()) {
if ($this->objectManager->get(DeploymentConfig::class)->isAvailable()) {
if (class_exists(\Magento\Setup\Console\CommandList::class)) {
$setupCommandList = new \Magento\Setup\Console\CommandList($this->serviceManager);
$commands = array_merge($commands, $setupCommandList->getCommands());
}

Copilot uses AI. Check for mistakes.

Comment on lines +181 to +184
if (class_exists(\Magento\Setup\Console\CommandList::class)) {
$setupCommandList = new \Magento\Setup\Console\CommandList($this->serviceManager);
$commands = array_merge($commands, $setupCommandList->getCommands());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain, why this change is needed?

Copy link
Contributor

@ihor-sviziev ihor-sviziev Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From reply in #40063 (comment):

We don't need these lines; it is probably due to a merge issue. You can
follow the logic. These lines don't change logic, just were touched by the
commit

These lines were added, so let's remove them if we don't need them

@Genaker
Copy link
Contributor Author

Genaker commented Jul 17, 2025 via email

@Genaker
Copy link
Contributor Author

Genaker commented Jul 17, 2025 via email

Copy link
Contributor

@ihor-sviziev ihor-sviziev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,
I've tried to debug a bit, and feels like the issue is coming from the masking original exception - if exception happened during loading list of the commands - we're just skipping it and trying to print the failure message after, but command is trying to be executed.
Ref:

protected function getApplicationCommands()
{
$commands = [];
try {
if ($this->objectManager->get(DeploymentConfig::class)->isAvailable()) {
/** @var CommandListInterface */
$commandList = $this->objectManager->create(CommandListInterface::class);
$commands = array_merge($commands, $commandList->getCommands());
}
$commands = array_merge(
$commands,
$this->getVendorCommands($this->objectManager)
);
} catch (\Exception $e) {
$this->initException = $e;
}
return $commands;
}

This code was introduced long time ago in 293ba1e

I think this logic has to be changed - we should not hide the exception, but print it directly when it happens - just remove try-catch in the getApplicationCommands method.

This will result in having a clear exception at the beginning:

$ php bin/magento -vvv

In ClassReader.php line 34:
                                                                  
  [ReflectionException (-1)]                                      
  Class "Test\TEsctCli\Console\Command\TestWrong" does not exist  
                                                                  

Exception trace:
  at /var/www/html/vendor/magento/framework/Code/Reader/ClassReader.php:34
 ReflectionClass->__construct() at /var/www/html/vendor/magento/framework/Code/Reader/ClassReader.php:34
 Magento\Framework\Code\Reader\ClassReader->getConstructor() at /var/www/html/vendor/magento/framework/ObjectManager/Definition/Runtime.php:50
 Magento\Framework\ObjectManager\Definition\Runtime->getParameters() at /var/www/html/vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php:48
 Magento\Framework\ObjectManager\Factory\Dynamic\Developer->create() at /var/www/html/vendor/magento/framework/ObjectManager/ObjectManager.php:73
 Magento\Framework\ObjectManager\ObjectManager->get() at /var/www/html/vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php:206
 Magento\Framework\ObjectManager\Factory\AbstractFactory->parseArray() at /var/www/html/vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php:182
 Magento\Framework\ObjectManager\Factory\AbstractFactory->resolveArgument() at /var/www/html/vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php:276
 Magento\Framework\ObjectManager\Factory\AbstractFactory->getResolvedArgument() at /var/www/html/vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php:239
 Magento\Framework\ObjectManager\Factory\AbstractFactory->resolveArgumentsInRuntime() at /var/www/html/vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php:34
 Magento\Framework\ObjectManager\Factory\Dynamic\Developer->_resolveArguments() at /var/www/html/vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php:59
 Magento\Framework\ObjectManager\Factory\Dynamic\Developer->create() at /var/www/html/vendor/magento/framework/ObjectManager/ObjectManager.php:59
 Magento\Framework\ObjectManager\ObjectManager->create() at /var/www/html/vendor/magento/framework/Console/Cli.php:156
 Magento\Framework\Console\Cli->getApplicationCommands() at /var/www/html/vendor/magento/framework/Console/Cli.php:137
 Magento\Framework\Console\Cli->getDefaultCommands() at /var/www/html/vendor/symfony/console/Application.php:1327
 Symfony\Component\Console\Application->init() at /var/www/html/vendor/symfony/console/Application.php:692
 Symfony\Component\Console\Application->find() at /var/www/html/vendor/symfony/console/Application.php:266
 Symfony\Component\Console\Application->doRun() at /var/www/html/vendor/magento/framework/Console/Cli.php:118
 Magento\Framework\Console\Cli->doRun() at /var/www/html/vendor/symfony/console/Application.php:175
 Symfony\Component\Console\Application->run() at /var/www/html/bin/magento:23

What do you think about it?

@Genaker
Copy link
Contributor Author

Genaker commented Jul 17, 2025

I've tried to debug a bit, and feels like the issue is coming from the masking original exception - if exception happened during loading list of the commands - we're just skipping it and trying to print the failure message after, but command is trying to be executed.
Ref:

Correct this approach works also but i would like to have more descriptive message to tell user something wrong not just default exception. For example check you extension not just : ReflectionException. Even we can check if it is file exist but has error, or it is file doesn't exist but in di.xml/ also we can show/log exception and continue or original command execution. Because after this you can't even cache clear you need do this manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Priority: P2 A defect with this priority could have functionality issues which are not to expectations. Progress: pending review Progress: review
Projects
Status: Pending Review
Development

Successfully merging this pull request may close these issues.

Ensure that a single command failure logs the error (file or stderr) without stopping the execution of subsequent CLI commands.
2 participants