ruvnet/ruflo · error

Agent ${agentId} not found

Error message

Agent ${agentId} not found

What it means

terminateAgent() looks up agentId in the agents map and throws when absent. Ids match exactly what spawnAgent() returned (explicit or agent-N form); there is no normalization. Because terminated agents remain in the map, terminating an already-terminated id does NOT throw — the throw means the id was never spawned (or belongs to another integration instance).

Source

Thrown at v3/@claude-flow/plugins/src/integrations/agentic-flow.ts:257

    this.agents.set(id, agent);

    this.emit(AGENTIC_FLOW_EVENTS.AGENT_SPAWNED, {
      agent,
      timestamp: new Date(),
    });

    this.config.logger?.info(`Agent spawned: ${id} (${options.type})`);

    return agent;
  }

  /**
   * Terminate an agent.
   */
  async terminateAgent(agentId: string): Promise<void> {
    const agent = this.agents.get(agentId);
    if (!agent) {
      throw new Error(`Agent ${agentId} not found`);
    }

    // Update agent status
    const terminatedAgent: SpawnedAgent = { ...agent, status: 'terminated' };
    this.agents.set(agentId, terminatedAgent);

    this.emit(AGENTIC_FLOW_EVENTS.AGENT_TERMINATED, {
      agentId,
      timestamp: new Date(),
    });

    this.config.logger?.info(`Agent terminated: ${agentId}`);
  }

  /**
   * Get agent by ID.
   */
  getAgent(agentId: string): SpawnedAgent | undefined {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Always use the SpawnedAgent.id captured from spawnAgent(); never reconstruct ids by string concatenation
  2. Route termination through a guard that treats 'not found' as already-gone success in cleanup code
  3. Keep one registry (Map<string, SpawnedAgent>) of live agents per integration and terminate only from it

Example fix

// before
await flow.terminateAgent(agentIdFromSomewhere); // Error: Agent ... not found

// after (cleanup-safe)
try {
  await flow.terminateAgent(agentId);
} catch (err) {
  if (!(err instanceof Error && /Agent .* not found/.test(err.message))) throw err;
  // already gone: nothing to do
}
Defensive patterns

Strategy: validation

Validate before calling

const live = new Map<string, SpawnedAgent>();
async function safeTerminate(id: string): Promise<void> {
  if (!live.has(id)) return; // already gone / never spawned: no-op
  await flow.terminateAgent(id);
  live.delete(id);
}

Try / catch

try {
  await flow.terminateAgent(agentId);
} catch (err) {
  if (err instanceof Error && /Agent .* not found/.test(err.message)) {
    // treat as already-terminated in cleanup paths
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Terminating with a typo'd or case-different id; reconstructing auto ids by string building ('agent-2') instead of using the returned SpawnedAgent.id; ids crossing serialization boundaries (JSON round-trip trimming); cleanup running after agents were spawned on a different instance.

Common situations: Shutdown handlers draining agents recorded from a previous run or another process; logs replayed into terminate calls; tests constructing a fresh integration but terminating ids from an earlier one.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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