ruvnet/ruflo · error
Cannot remove ${name}: required by ${dependents.join(', ')}
Error message
Cannot remove ${name}: required by ${dependents.join(', ')} What it means
Thrown by EnhancedPluginRegistry.unregister(name) when other plugins depend on the target (dependencyGraph.getDependents(name) is non-empty) and neither options.cascade nor options.force is set. The registry refuses to remove a plugin that would leave dependents with a broken dependency; cascade instead shuts dependents down first (reverse removal order), and force removes while only logging which links are broken.
Source
Thrown at v3/@claude-flow/plugins/src/registry/enhanced-plugin-registry.ts:370
}
// Check dependents
const dependents = this.dependencyGraph.getDependents(name);
if (dependents.length > 0) {
if (options?.cascade) {
// Unload dependents first (in reverse order)
const order = this.dependencyGraph.getRemovalOrder(name);
for (const dep of order) {
if (dep !== name) {
await this.shutdownPlugin(dep);
this.removePluginFromGraph(dep);
}
}
} else if (options?.force) {
this.logger.warn(`Force removing ${name}, breaking: ${dependents.join(', ')}`);
} else {
throw new Error(`Cannot remove ${name}: required by ${dependents.join(', ')}`);
}
}
// Shutdown and remove
await this.shutdownPlugin(name);
this.removePluginFromGraph(name);
this.logger.info(`Plugin unregistered: ${name}`);
}
// =========================================================================
// Initialization
// =========================================================================
/**
* Initialize all registered plugins.
*/
async initialize(): Promise<void> {View on GitHub (pinned to fa13ee4ad6)
Solutions
- Unregister dependents first (leaf plugins), then the shared plugin
- Pass { cascade: true } to remove the plugin together with its dependents in reverse order
- Pass { force: true } only when you accept breaking dependents at runtime (registry logs a warning) - e.g. forced shutdown
Example fix
// before
await registry.unregister('core-utils'); // 'my-plugin' depends on it -> throws
// after
await registry.unregister('core-utils', { cascade: true }); // removes dependents too
// or remove leaves first:
await registry.unregister('my-plugin');
await registry.unregister('core-utils'); Defensive patterns
Strategy: fallback
Validate before calling
const dependents = await getDependentsOf(registry, name); // mirror of dependencyGraph.getDependents
if (dependents.length > 0 && !options?.cascade && !options?.force) {
// remove leaves first instead of removing a required base
for (const dep of dependents) {
await registry.unregister(dep);
}
}
await registry.unregister(name); Try / catch
try {
await registry.unregister(name);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Cannot remove')) {
// rethrow as a typed signal so callers can choose cascade vs leaves-first
throw new PluginHasDependentsError(name, err.message);
}
throw err;
} Prevention
- Model plugin teardown order explicitly: dependents before dependencies
- Use { cascade: true } when removing a base plugin together with its dependents is intended
- Reserve { force: true } for shutdown paths; it knowingly breaks dependent links
When it happens
Trigger: unregister('core-utils') while 'my-plugin' declares a dependency on 'core-utils' via metadata.dependencies, without passing options; attempting teardown in the wrong order (shared plugin before its dependents); removing a base plugin during shutdown while feature plugins are still registered.
Common situations: Hot-swapping a shared library plugin that others import; partial unregisters during shutdown sequencing; test fixtures removing a common dependency while dependent plugins remain loaded.
Related errors
- Dependency validation failed: ${errorMessages}
- Invalid plugin: does not implement IPlugin interface
- Plugin ${name} already registered
- Maximum plugin limit (${this.config.maxPlugins}) reached
- Plugin ${name} requires core version >= ${resolvedPlugin.met
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/fd168626167d7bc5.
Report an issue: GitHub.