When a single failing or incompatible plugin crashes the entire host on startup, operators face a binary choice: disable all plugins or accept a broken application. Nine functioning plugins are blocked by the tenth's startup error. ISO 25010 reliability.availability requires that the system remain operational when individual components fail; graceful degradation is the mechanism that makes plugin system failures scope-contained. The absence of graceful degradation also makes plugin troubleshooting a binary-search exercise rather than a directed diagnosis.
High because an incompatible plugin that crashes the host on startup takes down all other plugins and the entire application, making one broken plugin an availability incident affecting all users.
Wrap each plugin's load and init sequence in an independent try/catch. Log the plugin name and failure reason, mark the plugin as disabled, and continue loading remaining plugins. Surface disabled plugins in an admin UI or startup log.
async function loadAllPlugins(manifests: PluginManifest[]): Promise<PluginLoadResult[]> {
const results: PluginLoadResult[] = [];
for (const manifest of manifests) {
try {
checkCompatibility(manifest);
await loadAndInit(manifest);
results.push({ name: manifest.name, status: 'loaded' });
} catch (error) {
this.logger.error(`Plugin "${manifest.name}" disabled: ${error.message}`);
results.push({ name: manifest.name, status: 'disabled', reason: error.message });
}
}
return results;
}
ID: plugin-extension-architecture.versioning-contracts.graceful-degradation
Severity: high
What to look for: Check what happens when a plugin is incompatible with the current host version, has a missing dependency, or fails during initialization. Look for:
Pass criteria: Count all graceful degradation paths. Incompatible or failing plugins are disabled with a clear, informative error message that includes: the plugin name, the reason for disabling (version mismatch, missing dependency, init error), and what action the user should take. The host and all other compatible plugins continue to function normally. At least 100% of plugin load failures must result in graceful degradation rather than host crashes.
Fail criteria: An incompatible or failing plugin crashes the host on startup. OR the plugin is silently skipped with no error message, logging, or notification — the user has no way to know the plugin isn't running. OR the host enters a degraded state that affects other plugins' functionality.
Skip (N/A) when: The plugin system has no compatibility checking (all plugins are always loaded). In this case, this check should fail, not skip — graceful degradation requires compatibility checking first.
Detail on fail: "When a plugin's init() throws an error, the entire host application crashes with an unhandled exception. Loading 10 plugins where 1 has a bug prevents all 9 working plugins from running. The error is a raw stack trace with no indication of which plugin caused it."
Remediation: Graceful degradation is what makes a plugin system production-ready. Users should never have to debug "which plugin is preventing my application from starting" by binary-search disabling plugins.
async function loadAllPlugins(manifests: PluginManifest[]): Promise<PluginLoadResult[]> {
const results: PluginLoadResult[] = [];
for (const manifest of manifests) {
try {
checkCompatibility(manifest); // throws if incompatible
await loadAndInit(manifest);
results.push({ name: manifest.name, status: 'loaded' });
} catch (error) {
this.logger.error(`Plugin "${manifest.name}" disabled: ${error.message}`);
results.push({ name: manifest.name, status: 'disabled', reason: error.message });
// Continue loading other plugins
}
}
return results;
}