A plugin invocation loop without per-handler try/catch means a single third-party plugin throwing an uncaught error crashes the host, stops subsequent handlers from running, and surfaces as a 500 to every user — not just the user whose request triggered the broken plugin. CWE-755 (Improper Exception Handling) and ISO 25010 reliability.fault-tolerance both require that component failures be contained. The economic impact is proportional: one buggy plugin in an ecosystem of ten takes down applications that have nine functioning plugins installed.
Critical because an unguarded hook invocation loop lets a single plugin error crash the host and stop all other plugins' handlers from executing, making application reliability contingent on the worst plugin installed.
Wrap each handler invocation in an individual try/catch inside the loop. Log the plugin name and hook name alongside the error so operators can identify the offending plugin without a stack trace investigation.
async function invokeHook(name: string, context: HookContext): Promise<void> {
for (const { pluginName, handler } of this.getHandlers(name)) {
try {
await handler(context);
} catch (error) {
this.logger.error(`Plugin "${pluginName}" threw in hook "${name}":`, error);
this.emit('plugin:error', { pluginName, hook: name, error });
// Continue to next handler
}
}
}
ID: plugin-extension-architecture.isolation-security.error-boundaries
Severity: critical
What to look for: Examine how the host invokes plugin code. Check whether each plugin's handlers are wrapped in try/catch (or equivalent error handling). Specifically look for:
avvio — plugin errors are containedPass criteria: Count all hook execution points with error handling. Plugin code execution is wrapped in error boundaries. A thrown error in one plugin does not crash the host application or prevent other plugins from running. The host logs or reports which plugin caused the error. At minimum, the hook invocation loop catches errors per-handler. 100% of hook execution paths must have error boundaries that prevent plugin failures from crashing the host.
Fail criteria: Must not pass when a single plugin error can crash the entire host application. Plugin handlers run without try/catch. An error thrown in any plugin's handler propagates up and crashes the host, stops the request, or prevents other plugins' handlers from executing. OR errors are caught but silently swallowed with no logging (impossible to debug which plugin is failing).
Skip (N/A) when: The system runs only first-party plugins that are tested as part of the host's CI (no third-party plugin support). Even then, consider marking as fail rather than skip for robustness.
Detail on fail: "The invokeHook() function iterates handlers in a for loop with no try/catch. If the second of five handlers throws, handlers 3-5 never execute and the error propagates to the request handler, returning a 500 to the user. One broken plugin breaks the entire application for all users."
Remediation: Error boundaries are non-negotiable for any plugin system that accepts third-party code. Without them, your application's reliability is only as good as the worst plugin installed.
async function invokeHook(name: string, context: HookContext): Promise<void> {
for (const { pluginName, handler } of this.getHandlers(name)) {
try {
await handler(context);
} catch (error) {
this.logger.error(`Plugin "${pluginName}" threw in hook "${name}":`, error);
this.emit('plugin:error', { pluginName, hook: name, error });
// Continue to next handler — don't let one plugin break others
}
}
}
Cross-reference: For resource cleanup on plugin failure, see the resource-cleanup check in the Isolation & Security category.