{"record":{"id":"21dcae58e3b13bbe","repo":"ruvnet/ruflo","slug":"agent-agentid-not-found","errorCode":null,"errorMessage":"Agent ${agentId} not found","messagePattern":"Agent (.+?) not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/integrations/agentic-flow.ts","lineNumber":257,"sourceCode":"    this.agents.set(id, agent);\n\n    this.emit(AGENTIC_FLOW_EVENTS.AGENT_SPAWNED, {\n      agent,\n      timestamp: new Date(),\n    });\n\n    this.config.logger?.info(`Agent spawned: ${id} (${options.type})`);\n\n    return agent;\n  }\n\n  /**\n   * Terminate an agent.\n   */\n  async terminateAgent(agentId: string): Promise<void> {\n    const agent = this.agents.get(agentId);\n    if (!agent) {\n      throw new Error(`Agent ${agentId} not found`);\n    }\n\n    // Update agent status\n    const terminatedAgent: SpawnedAgent = { ...agent, status: 'terminated' };\n    this.agents.set(agentId, terminatedAgent);\n\n    this.emit(AGENTIC_FLOW_EVENTS.AGENT_TERMINATED, {\n      agentId,\n      timestamp: new Date(),\n    });\n\n    this.config.logger?.info(`Agent terminated: ${agentId}`);\n  }\n\n  /**\n   * Get agent by ID.\n   */\n  getAgent(agentId: string): SpawnedAgent | undefined {","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/integrations/agentic-flow.ts#L239-L275","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Always use the SpawnedAgent.id captured from spawnAgent(); never reconstruct ids by string concatenation","Route termination through a guard that treats 'not found' as already-gone success in cleanup code","Keep one registry (Map<string, SpawnedAgent>) of live agents per integration and terminate only from it"],"exampleFix":"// before\nawait flow.terminateAgent(agentIdFromSomewhere); // Error: Agent ... not found\n\n// after (cleanup-safe)\ntry {\n  await flow.terminateAgent(agentId);\n} catch (err) {\n  if (!(err instanceof Error && /Agent .* not found/.test(err.message))) throw err;\n  // already gone: nothing to do\n}","handlingStrategy":"validation","validationCode":"const live = new Map<string, SpawnedAgent>();\nasync function safeTerminate(id: string): Promise<void> {\n  if (!live.has(id)) return; // already gone / never spawned: no-op\n  await flow.terminateAgent(id);\n  live.delete(id);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await flow.terminateAgent(agentId);\n} catch (err) {\n  if (err instanceof Error && /Agent .* not found/.test(err.message)) {\n    // treat as already-terminated in cleanup paths\n  } else {\n    throw err;\n  }\n}","preventionTips":["Always terminate with the SpawnedAgent.id returned by spawnAgent()","Never reconstruct auto ids by string concatenation","Make cleanup idempotent: tolerate not-found instead of failing teardown"],"tags":["swarm","agents","shutdown","id-handling"],"backgroundTag":"resource-not-found","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}