passbolt/passbolt_api · error · InternalErrorException
The class should implement PluginInterface::class.
Error message
The class {$name} should implement PluginInterface::class. What it means
FeaturePluginAwareTrait::getPluginEnabledConfigurationKey() resolves the config key that controls whether a feature plugin is enabled. If the given $name is an existing PHP class, it must implement Cake\Core\PluginInterface; otherwise an InternalErrorException is thrown, because the trait can only derive the plugin name from valid plugin classes or plain plugin names.
Solutions
- Ensure the class passed implements Cake\Core\PluginInterface (e.g. PassboltCe/Folders/src/FoldersPlugin.php).
- Alternatively pass the short plugin name string ('Folders') instead of the FQCN.
- Check for typos or wrong namespaces in the feature-plugin configuration values.
- Verify plugin class naming convention: class name must end with 'Plugin' so the short name can be extracted.
Example fix
// before
$this->isFeaturePluginEnabled('\App\Some\NotAPluginClass');
// after
$this->isFeaturePluginEnabled('\PassboltCe\Folders\FoldersPlugin'); // implements PluginInterface
// or simply:
$this->isFeaturePluginEnabled('Folders'); Defensive patterns
Strategy: validation
Validate before calling
if (class_exists($plugin)) {
if (!is_subclass_of($plugin, \Cake\Core\PluginInterface::class)) {
throw new \InvalidArgumentException("$plugin must implement PluginInterface");
}
} Type guard
function isPluginClass(string $name): bool {
return !class_exists($name) || is_subclass_of($name, \Cake\Core\PluginInterface::class);
} Try / catch
try {
$this->isFeaturePluginEnabled($plugin);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
Log::error('Invalid plugin class: ' . $e->getMessage());
$this->isFeaturePluginEnabled(substr($plugin, strrpos($plugin, '\\') + 1));
} Prevention
- Always pass either a short plugin name or a class FQCN that implements PluginInterface.
- Centralize plugin FQCN references in a constants class to avoid typos.
- Add a unit test asserting every configured feature plugin class implements PluginInterface.
- Review config/passbolt.php feature plugin entries after refactors/renames.
When it happens
Trigger: Passing a fully-qualified class name (e.g. in enableFeaturePlugin/disableFeaturePlugin or isFeaturePluginEnabled) that exists but does not implement PluginInterface — such as a random class in the same namespace, a typo'd class, or a class name with a truncated/wrong suffix.
Common situations: Configuring feature plugins in config/passbolt.php with a class string that points at the wrong class; refactoring renamed the plugin class so it no longer implements PluginInterface; copy-pasted a namespace that resolves to a non-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 is not a valid plugin.
- 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/38d5fe355929859a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Utility/Application/FeaturePluginAwareTrait.php:62
/**
* @param string $name Plugin class name or plugin name, either upper case or lower case first (without the "Passbolt/" prefix)
* @return void
*/
public function disableFeaturePlugin(string $name): void
{
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)