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

  1. Ensure the class passed implements Cake\Core\PluginInterface (e.g. PassboltCe/Folders/src/FoldersPlugin.php).
  2. Alternatively pass the short plugin name string ('Folders') instead of the FQCN.
  3. Check for typos or wrong namespaces in the feature-plugin configuration values.
  4. 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

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


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)