ruvnet/ruflo · error · Error
Plugin ${this.metadata.name} already initialized
Error message
Plugin ${this.metadata.name} already initialized What it means
initialize() only accepts plugins in the 'uninitialized' state; any second call throws 'already initialized'. The state flips to 'initializing' immediately upon entry, before validation runs, so even retrying a failed initialize() on the same instance is rejected. This is a strict one-shot lifecycle machine, not an idempotent init.
Source
Thrown at v3/@claude-flow/plugins/src/core/base-plugin.ts:146
protected get services(): ServiceContainer {
return this.context.services;
}
protected get settings(): Record<string, unknown> {
return this.config.settings;
}
// =========================================================================
// Lifecycle Implementation
// =========================================================================
/**
* Initialize the plugin.
* Subclasses should override onInitialize() instead of this method.
*/
async initialize(context: PluginContext): Promise<void> {
if (this._state !== 'uninitialized') {
throw new Error(`Plugin ${this.metadata.name} already initialized`);
}
this.setState('initializing');
this._context = context;
this._initTime = new Date();
try {
// Validate dependencies
await this.validateDependencies();
// Validate configuration
await this.validateConfig();
// Call subclass initialization
await this.onInitialize();
this.setState('initialized');
this.eventBus.emit(PLUGIN_EVENTS.INITIALIZED, { plugin: this.metadata.name });View on GitHub (pinned to fa13ee4ad6)
Solutions
- Check the plugin's state before initializing and skip when it is not 'uninitialized'
- Create a fresh plugin instance for each initialization instead of reusing one (rebuild the plugin factory result)
- Wrap init in a once() memoization at the call site so repeated invocations share the first promise
Example fix
// before await plugin.initialize(ctx); // ...later, somewhere else: await plugin.initialize(ctx); // Error: already initialized // after const initPlugin = once(() => plugin.initialize(ctx)); await initPlugin(); // first call wins; later calls are no-ops
Defensive patterns
Strategy: validation
Validate before calling
import once from 'lodash/once'; // or a hand-rolled memo
const initPlugin = once(() => plugin.initialize(context));
await initPlugin(); // safe to call from every boot path
// explicit state check when once() is not available:
if (plugin.state === 'uninitialized') {
await plugin.initialize(context);
} Type guard
type Initializable = { state?: string };
function canInitialize(p: Initializable): boolean {
return p.state === 'uninitialized';
} Prevention
- Initialize each plugin exactly once; funnel all init paths through one function
- Create a fresh instance (via the plugin factory) when a re-init is genuinely needed
- Guard dev-mode double-mounts (StrictMode/HMR) with once() wrappers
When it happens
Trigger: Calling plugin.initialize() or registry.initialize() twice on the same plugin instance; retry wrappers that re-run initialize after a transient failure; dev-mode double-mounting (React StrictMode effects, HMR) that executes setup code twice; registering one shared plugin object with two registries.
Common situations: Fire-and-forget init plus an explicit init elsewhere in boot code; retry-on-error logic around initialization; module singletons surviving hot reloads while init code re-runs.
Related errors
- Can only resume paused agent
- Plugin ${this.metadata.name} not initialized
- Swarm already initialized
- Registry already initialized
- Cannot start terminated agent
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/7e8556500f739142.
Report an issue: GitHub.