ruvnet/ruflo · error

Daemon '${config.name}' is already registered

Error message

Daemon '${config.name}' is already registered

What it means

DaemonManager.register(config, task) throws when a daemon with the same config.name is already present in its internal map. Names are the primary key for start/stop/updateInterval, so double registration is treated as a programming error (usually duplicated setup code) rather than silently replacing the task.

Source

Thrown at v3/@claude-flow/hooks/src/daemons/index.ts:56

/**
 * Daemon Manager - controls background daemon processes
 */
export class DaemonManager {
  private config: DaemonManagerConfig;
  private daemons: Map<string, DaemonInstance> = new Map();
  private restartCounts: Map<string, number> = new Map();

  constructor(config?: Partial<DaemonManagerConfig>) {
    this.config = { ...DEFAULT_CONFIG, ...config };
  }

  /**
   * Register a daemon
   */
  register(config: DaemonConfig, task: () => Promise<void>): void {
    if (this.daemons.has(config.name)) {
      throw new Error(`Daemon '${config.name}' is already registered`);
    }

    const state: DaemonState = {
      name: config.name,
      status: 'stopped',
      executionCount: 0,
      failureCount: 0,
    };

    this.daemons.set(config.name, { config, state, task });
  }

  /**
   * Start a daemon
   */
  async start(name: string): Promise<void> {
    const daemon = this.daemons.get(name);
    if (!daemon) {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Guard registration: if (manager.getState(name) === undefined) manager.register(cfg, task) — getState is the public existence probe
  2. Move all daemon registration into one composition root that runs exactly once
  3. For dev/HMR, dispose the old manager (stopAll) or key managers per reload generation before re-registering

Example fix

// before
manager.register({ name: 'metrics', interval: 60_000, enabled: true }, task); // 2nd call throws

// after
if (manager.getState('metrics') === undefined) {
  manager.register({ name: 'metrics', interval: 60_000, enabled: true }, task);
}
Defensive patterns

Strategy: validation

Validate before calling

// Idempotent registration using the public state probe
function registerOnce(manager: DaemonManager, cfg: DaemonConfig, task: () => Promise<void>): void {
  if (manager.getState(cfg.name) === undefined) {
    manager.register(cfg, task);
  }
}

Try / catch

try {
  manager.register(cfg, task);
} catch (e) {
  if (e instanceof Error && e.message.includes('already registered')) {
    // benign in HMR/retry flows: keep the existing registration
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling register() twice with the same name — e.g. a module-level init running again during hot module reload, a plugin and the host app both registering 'memory-consolidation', or an init routine retried after partial failure.

Common situations: Dev servers with HMR re-executing setup modules; manager initialized in several files instead of a single composition root; retry wrappers around bootstrap code; tests that re-use a module-scoped manager across cases.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/bf57fca66beaee3c. Report an issue: GitHub.