getgrav/grav · error · CommandNotFoundException
The command "%s" does not exist.
Error message
The command "%s" does not exist.
What it means
Thrown by Grav's PluginCommandLoader::get() when a command name is requested that the loader never registered. The loader discovers plugin CLI commands by scanning plugins://<name>/cli/ for files matching [A-Z]\w+Command.php whose class lives in the Grav\Plugin\Console namespace and extends Symfony\Component\Console\Command; only each command's getName() value and its aliases become addressable names. Requesting anything else raises this CommandNotFoundException.
Source
Thrown at system/src/Grav/Console/Application/CommandLoader/PluginCommandLoader.php:81
if (isset($aliases)) {
foreach ($aliases as $alias) {
$this->commands[$alias] = $command;
}
}
}
}
}
}
/**
* @param string $name
* @return Command
*/
public function get($name): Command
{
$command = $this->commands[$name] ?? null;
if (null === $command) {
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
}
return $command;
}
/**
* @param string $name
* @return bool
*/
public function has($name): bool
{
return isset($this->commands[$name]);
}
/**
* @return string[]
*/
public function getNames(): arrayView on GitHub (pinned to 6040efed04)
Solutions
- List what the loader actually registered: run `bin/plugin <plugin> list` (or call getNames() on the loader) and use the exact command name shown.
- Check the command file: it must live in user/plugins/<plugin>/cli/, match [A-Z]\w+Command.php, contain class Grav\Plugin\Console\<FileNameWithoutDotPhp>, and extend Symfony\Component\Console\Command.
- Verify the registered name: inspect the command's configure() — the name passed to setName() (or the default derived name) is what you must type; register aliases via setAliases() if you need alternate spellings.
- If the plugin ships no cli/ folder, the plugin exposes no CLI commands — nothing to invoke; check the plugin's docs for its actual console entry point.
Example fix
// file user/plugins/myplugin/cli/DoThingCommand.php
// before: class DoStuff extends Command { ... } // wrong class name -> never loaded
class DoThingCommand extends Command {
protected function configure(): void
{
$this->setName('do-thing');
}
}
// after: `bin/plugin myplugin do-thing` now resolves via PluginCommandLoader Defensive patterns
Strategy: validation
Validate before calling
// before calling get()
$names = $loader->getNames();
if (!$loader->has($commandName)) {
fwrite(STDERR, "Unknown command. Available: " . implode(', ', $names) . PHP_EOL);
exit(1);
}
$command = $loader->get($commandName); Try / catch
try {
$command = $loader->get($name);
} catch (\Symfony\Component\Console\Exception\CommandNotFoundException $e) {
// degrade gracefully: list available commands
$output->writeln('<error>' . $e->getMessage() . '</error>');
$output->writeln('Available: ' . implode(', ', $loader->getNames()));
return Command::FAILURE;
} Prevention
- Always gate PluginCommandLoader::get() with has($name).
- Derive command names from getName() at runtime instead of hardcoding strings.
- Follow the [A-Z]\w+Command.php + Grav\Plugin\Console namespace convention in every plugin's cli/ folder.
When it happens
Trigger: Running `bin/plugin <plugin> <command>` where <command> is not the name returned by the command class's getName() (e.g. the file is ClearCacheCommand.php but getName() returns 'clear-cache' and you type 'clearcache'); the plugin has no cli/ subfolder at all; the PHP file/class namespace is wrong (not Grav\Plugin\Console\<Filename>), so class_exists() fails and nothing is registered; or using an alias that the command never declared.
Common situations: Typo in the command name on the CLI; plugin upgraded and a command was renamed or removed; command file naming convention violated (lowercase first letter, missing 'Command' suffix); command class in the wrong namespace after a copy-paste from another plugin; invoking `bin/plugin <plugin> list` expecting a command that the plugin simply does not ship.
Related errors
- Plugin "{$this->pluginName}" is not installed.
- Plugin "{$this->pluginName}" is not enabled.
- Internal Error
- No backups defined...
- Backup location: {$backup_root} does not exist...
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/3997e2464cc82f97.
Report an issue: GitHub.