passbolt/passbolt_api · error · InternalErrorException
The class is not a valid plugin.
Error message
The class {$name} is not a valid plugin. What it means
After confirming $name is a valid PluginInterface subclass, getPluginEnabledConfigurationKey() strips the namespace and the trailing 'Plugin' suffix (substr(..., 0, -6)) to derive the short plugin name. If the result is empty — meaning the class name itself is literally 'Plugin' or the extraction yields nothing — an InternalErrorException is thrown since no config key can be built.
Solutions
- Pass the concrete plugin class (e.g. \PassboltCe\Folders\FoldersPlugin) instead of a base/generic class named 'Plugin'.
- Pass the plain short plugin name string instead of a class name.
- Rename the class so it follows the '<Name>Plugin' convention with a non-empty short name.
Example fix
// before
$this->enableFeaturePlugin('\My\Namespace\Plugin'); // basename 'Plugin' -> empty short name
// after
$this->enableFeaturePlugin('\My\Namespace\MyFeaturePlugin'); Defensive patterns
Strategy: validation
Validate before calling
$short = substr(strrchr($plugin, '\\') ?: $plugin, 1);
if ($short === '' || $short === 'Plugin') {
throw new \InvalidArgumentException("$plugin is not a concrete plugin class");
} Try / catch
try {
$this->enableFeaturePlugin($plugin);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
if (str_contains($e->getMessage(), 'is not a valid plugin')) {
Log::error('Plugin class name yields empty short name: ' . $plugin);
}
throw $e;
} Prevention
- Name plugin classes with a meaningful prefix before the 'Plugin' suffix (e.g. FoldersPlugin, never just Plugin).
- Never pass abstract base classes to feature plugin enable/disable helpers.
- Add static analysis rules flagging classes named exactly 'Plugin'.
- Prefer short plugin name strings when the class FQCN is unnecessary.
When it happens
Trigger: Passing a class whose basename is exactly 'Plugin' (strrchr finds '\', substr strips the last 6 chars 'Plugin' leaving empty string); e.g. a class named just \Some\Plugin that implements PluginInterface.
Common situations: An abstract base plugin class named 'Plugin' or a generically-named class accidentally passed to isFeaturePluginEnabled/enableFeaturePlugin/disableFeaturePlugin instead of the concrete plugin class.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- The class should implement PluginInterface::class.
- Could not enable Duo MFA provider.
- Could not enable Duo MFA provider.
- Could not login using Duo MFA provider.
- Invalid public key validation rules are missing.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/63e5cee758384f31.
Report an issue: GitHub.
Appendix: source
Thrown at src/Utility/Application/FeaturePluginAwareTrait.php:67
{
Configure::write($this->getPluginEnabledConfigurationKey($name), false);
}
/**
* @param string $name Plugin class name or plugin name, either upper case or lower case first (without the "Passbolt/" prefix)
* @return string
* @throws \Cake\Http\Exception\InternalErrorException if the plugin name is a class and not a plugin interface
*/
protected function getPluginEnabledConfigurationKey(string $name): string
{
if (class_exists($name)) {
if (!is_subclass_of($name, PluginInterface::class)) {
throw new InternalErrorException("The class {$name} should implement PluginInterface::class.");
}
$extractedName = substr(substr(strrchr($name, '\\'), 1), 0, -6);
if (empty($extractedName)) {
throw new InternalErrorException("The class {$name} is not a valid plugin.");
}
$name = $extractedName;
}
$name = lcfirst($name);
return "passbolt.plugins.{$name}.enabled";
}
}
View on GitHub (pinned to 31c1bbc10f)