{"record":{"id":"a46f397d9c9ab3af","repo":"ruvnet/ruflo","slug":"wasm-agent-not-found-agentid","errorCode":null,"errorMessage":"WASM agent not found: ${agentId}","messagePattern":"WASM agent not found: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/ruvector/agent-wasm.ts","lineNumber":195,"sourceCode":"}\n\n/**\n * Send a prompt to a WASM agent.\n *\n * ADR-129 P1: JsModelProvider is now wired at creation time so the WASM\n * agent's internal conversation loop (multi-turn state, turn_count,\n * stop conditions) runs against a real LLM.  The echo-stub detection\n * block is kept as a fallback for keyless environments (CI, sandboxed\n * test runners) — behaviour is identical to the pre-P1 path when no\n * provider key is set.\n *\n * Billing note: every wasm_agent_prompt call with a provider key\n * configured makes a billable LLM call.  Use a keyless environment to\n * get the echo stub for cost-free sandboxing.\n */\nexport async function promptWasmAgent(agentId: string, input: string): Promise<string> {\n  const entry = agents.get(agentId);\n  if (!entry) throw new Error(`WASM agent not found: ${agentId}`);\n\n  entry.info.state = 'running';\n  try {\n    const wasmResult = await entry.agent.prompt(input);\n    entry.info.state = 'idle';\n    syncAgentInfo(entry);\n\n    // Detect the WASM echo stub (present when no JsModelProvider was\n    // attached, i.e. keyless environments).\n    const isEchoStub = typeof wasmResult === 'string' &&\n      (wasmResult === `echo: ${input}` || /^echo: /.test(wasmResult.slice(0, 12)));\n\n    if (!isEchoStub) {\n      // JsModelProvider routed through the v3 provider system — return\n      // the real response.  turn_count was already incremented by the\n      // WASM runtime.\n      return wasmResult;\n    }","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/ruvector/agent-wasm.ts#L177-L213","documentation":"Thrown by promptWasmAgent(agentId, input) when agents.get(agentId) returns undefined — the agentId is not in the in-memory agent registry. The registry is a module-level Map keyed by IDs like 'wasm-agent-3-lkjx8', so an unknown ID means the agent was never created in this process, was already terminated, or the process restarted (the Map is not persisted).","triggerScenarios":"Calling promptWasmAgent with an ID returned from a previous process run (after restart); passing an ID from listWasmAgents() after terminateWasmAgent was called on it; typo or copy-paste error in the agentId string; holding an ID across an await that outlived a terminate call elsewhere; using an ID from a different worker thread (the Map is per-process).","commonSituations":"Long-running server that creates an agent, restarts (deploy/restart), and a client retries with the old ID; CLI command creates an agent in one invocation and tries to prompt it in the next (separate processes); concurrent code path calls terminateWasmAgent while another caller still holds the ID; ID was persisted to disk via exportWasmState and reloaded, but the in-memory agent was not.","solutions":["Call listWasmAgents() before prompting to confirm the ID is still live in this process.","If you need agent continuity across process restarts, use exportWasmState() to save and rehydrate — but note the underlying WasmAgent handle itself is not serializable; you must re-create.","Treat the ID as ephemeral: create, use, and terminate within a single process lifetime.","Check for a stray terminateWasmAgent call (including in error-handling paths) that removes the agent before the prompt."],"exampleFix":"// before — assumes ID is still valid\nconst out = await promptWasmAgent(staleId, input);\n\n// after — verify liveness, recreate if gone\nconst live = listWasmAgents().some(a => a.id === staleId);\nif (!live) {\n  const info = await createWasmAgent(config);\n  return promptWasmAgent(info.id, input);\n}\nreturn promptWasmAgent(staleId, input);","handlingStrategy":"type-guard","validationCode":"import { listWasmAgents, getWasmAgent } from './agent-wasm';\n\nfunction assertAgentLive(agentId: string): void {\n  if (!getWasmAgent(agentId)) {\n    throw new Error(\n      `agent ${agentId} not live. Active: ${listWasmAgents().map(a => a.id).join(', ') || '(none)'}`\n    );\n  }\n}\n\nassertAgentLive(agentId);\nconst out = await promptWasmAgent(agentId, input);","typeGuard":"function isLiveAgentId(id: string): boolean {\n  return getWasmAgent(id) !== null;\n}","tryCatchPattern":"try {\n  return await promptWasmAgent(agentId, input);\n} catch (e) {\n  if (/WASM agent not found/.test(String(e))) {\n    // Recreate and retry once — agent IDs are ephemeral.\n    const info = await createWasmAgent(config);\n    return promptWasmAgent(info.id, input);\n  }\n  throw e;\n}","preventionTips":["Treat agent IDs as ephemeral — valid only within the process that created them.","Snap listWasmAgents() before batch operations rather than probing stale IDs.","Serialize terminateWasmAgent with readers to avoid freeing an agent mid-prompt.","For long-lived services, design clients to recover from 'agent not found' by recreating."],"tags":["agent-lifecycle","wasm","in-memory-state","registry"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}