ruvnet/ruflo · error · PluginError
DUPLICATE_PLUGIN
DUPLICATE_PLUGIN
Error message
Plugin '${plugin.name}' is already loaded What it means
PluginLoader.loadPlugin(plugin, context) first validates the plugin interface, then registers it by unique name; if the registry already has a plugin with the same name it throws PluginError with code 'DUPLICATE_PLUGIN' before any initialization runs (PluginError carries pluginName and code fields). Loading the same plugin object - or a different object with the same name - twice on one loader is rejected.
Source
Thrown at v3/@claude-flow/shared/src/plugin-loader.ts:99
private registry: PluginRegistry;
private initializationOrder: string[] = [];
private healthCheckIntervalId?: NodeJS.Timeout;
constructor(registry: PluginRegistry, config?: PluginLoaderConfig) {
this.registry = registry;
this.config = { ...DEFAULT_CONFIG, ...config };
}
/**
* Load a single plugin
*/
async loadPlugin(plugin: ClaudeFlowPlugin, context: PluginContext): Promise<void> {
// Validate plugin
this.validatePlugin(plugin);
// Check for duplicates
if (this.registry.hasPlugin(plugin.name)) {
throw new PluginError(
`Plugin '${plugin.name}' is already loaded`,
plugin.name,
'DUPLICATE_PLUGIN'
);
}
// Register plugin in uninitialized state
this.registry.registerPlugin(plugin, 'uninitialized', context);
// Resolve dependencies
if (this.config.strictDependencies) {
this.validateDependencies(plugin);
}
// Initialize plugin
await this.initializePlugin(plugin, context);
// Update initialization orderView on GitHub (pinned to fa13ee4ad6)
Solutions
- Deduplicate the list by name before loading: [...new Map(plugins.map(p => [p.name, p])).values()]
- Make plugin loading single-shot by memoizing the loadPlugins call
- If overlap is expected, catch PluginError with code 'DUPLICATE_PLUGIN' and treat that specific plugin as already-loaded success
Example fix
// before await loader.loadPlugins([authPlugin, authPlugin]); // second entry throws // after const unique = [...new Map(plugins.map(p => [p.name, p])).values()]; await loader.loadPlugins(unique);
Defensive patterns
Strategy: validation
Validate before calling
// dedupe by name before loading - registry keys are plugin names const uniquePlugins = [...new Map(plugins.map(p => [p.name, p])).values()]; await loader.loadPlugins(uniquePlugins);
Try / catch
import { PluginError } from '@claude-flow/shared/dist/plugin-interface';
try {
await loader.loadPlugin(plugin, ctx);
} catch (e) {
if (e instanceof PluginError && e.code === 'DUPLICATE_PLUGIN') {
return; // same plugin already registered - treat as idempotent
}
throw e;
} Prevention
- Deduplicate plugin lists by name before loadPlugins
- Memoize the loadPlugins call so init code cannot run twice
- Use PluginError.code (not message parsing) to branch on plugin-load failures
When it happens
Trigger: loadPlugins([p, p]) with the same plugin listed twice; two entries registering 'my-plugin' (a local copy and an npm copy); calling loadPlugin again in retry code after a partial success; HMR re-running the registration module with the same plugin definitions.
Common situations: Monorepos bundling the same plugin twice; re-entrant init code; plugin name collisions between teams; tests reusing a loader across cases.
Related errors
- Collection ${collection.id} already loaded
- Cannot disable ${pluginName}: other plugins may depend on it
- INVALID_PLUGIN
- DEPENDENCY_NOT_FOUND
- MCP initialization failed: ${initResponse.error.message}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/06b9912d43182893.
Report an issue: GitHub.