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
- Guard registration: if (manager.getState(name) === undefined) manager.register(cfg, task) — getState is the public existence probe
- Move all daemon registration into one composition root that runs exactly once
- 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
- Register all daemons in one composition root executed exactly once
- For dev HMR, stop and rebuild the manager per reload instead of re-registering into it
- Use shared name constants to avoid accidental collisions between modules
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
- Daemon '${name}' not found
- Daemon '${name}' is disabled
- Can only resume paused agent
- SSRF guard: invalid URL — ${rawUrl}
- User token not found
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/bf57fca66beaee3c.
Report an issue: GitHub.