{"record":{"id":"a673bde4be58c49b","repo":"ruvnet/ruflo","slug":"result-error-provider-call-failed","errorCode":null,"errorMessage":"${result.error ?? 'provider call failed'}","messagePattern":"\\$\\{result\\.error \\?\\? 'provider call failed'\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/ruvector/agent-wasm.ts","lineNumber":172,"sourceCode":" * Called once at agent-creation time; the provider stays attached for the\n * agent's lifetime.  No-op (returns false) when no provider keys are\n * configured so the echo-fallback path below is preserved for keyless\n * environments.\n */\nasync function attachJsModelProvider(agent: any, config: WasmAgentConfig): Promise<boolean> {\n  const hasAny = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENROUTER_API_KEY || process.env.OLLAMA_API_KEY);\n  if (!hasAny) return false;\n  const mod = await import('@ruvector/rvagent-wasm');\n  const { callAnthropicMessages, resolveAnthropicModel } = await import('../mcp-tools/agent-execute-core.js');\n  const model = resolveAnthropicModel(config.model);\n  const systemPrompt = config.instructions || 'You are a helpful coding assistant running in a Ruflo WASM agent sandbox.';\n\n  const provider = new mod.JsModelProvider(async (messagesJson: string) => {\n    const messages: Array<{ role: string; content: string }> = JSON.parse(messagesJson);\n    const lastUser = [...messages].reverse().find(m => m.role === 'user');\n    const prompt = lastUser?.content ?? messagesJson;\n    const result = await callAnthropicMessages({ prompt, systemPrompt, model, maxTokens: 2048 });\n    if (!result.success) throw new Error(result.error ?? 'provider call failed');\n    return JSON.stringify({ role: 'assistant', content: result.output ?? '' });\n  });\n  agent.set_model_provider(provider);\n  return true;\n}\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","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/ruvector/agent-wasm.ts#L154-L190","documentation":"Thrown inside the JsModelProvider callback wired to a WasmAgent: the v3 provider call (callAnthropicMessages) returned { success: false }, so the callback throws to signal failure to the WASM runtime. The message is result.error when present, else the literal 'provider call failed'. Because the provider is attached at agent-creation time only when ANTHROPIC_API_KEY / OPENROUTER_API_KEY / OLLAMA_API_KEY is set, this only fires in a keyed environment — every prompt is a real billable call.","triggerScenarios":"API key is set but invalid/expired (401); rate limit hit (429); requested model not available on the configured provider; RUFLO_PROVIDER points at a provider whose endpoint is unreachable; OpenRouter routing fails for an uncommon model; Ollama key set but ollama daemon not running; network proxy blocks the provider endpoint.","commonSituations":"ANTHROPIC_API_KEY rotated in the dashboard but the process still holds the old value; free-tier OpenRouter key hit its daily cap; model string like 'anthropic:claude-sonnet-4-6' resolved to a model the account doesn't have access to; running in CI behind a corporate firewall that blocks api.anthropic.com; transient provider outage during a long multi-turn agent run.","solutions":["Inspect the full result.error (the message interpolates it) — a 401 means rotate the key, 429 means back off, 'model not found' means fix the model string.","Verify the env var the process actually sees: console.log(Boolean(process.env.ANTHROPIC_API_KEY)) — dotenv may not have loaded in this entrypoint.","For transient failures (429, 5xx), wrap the prompt call in a retry with exponential backoff; the WASM runtime does not retry on its own.","For local/offline runs, unset all three provider keys so the echo-stub fallback engages instead of failing through the provider path."],"exampleFix":"// before — provider error surfaces as a thrown Error mid-prompt\nconst out = await promptWasmAgent(agentId, input);\n\n// after — catch provider failures and fall back gracefully\ntry {\n  const out = await promptWasmAgent(agentId, input);\n} catch (e) {\n  if (/provider call failed|rate.?limit|unauthorized|401|429/i.test(String(e))) {\n    // surface a user-actionable message instead of crashing the agent loop\n    throw new Error(`LLM provider unreachable: ${e}. Check ANTHROPIC_API_KEY and provider status.`);\n  }\n  throw e;\n}","handlingStrategy":"retry","validationCode":"function hasProviderKey(): boolean {\n  return !!(process.env.ANTHROPIC_API_KEY || process.env.OPENROUTER_API_KEY || process.env.OLLAMA_API_KEY);\n}\n\n// If you don't want billable calls (or provider failures), run keyless to get the echo stub.\n// If you do, preflight the key with a cheap call:\nasync function preflightProvider(): Promise<boolean> {\n  const { callAnthropicMessages } = await import('../mcp-tools/agent-execute-core.js');\n  const r = await callAnthropicMessages({ prompt: 'ping', systemPrompt: '', model: resolveAnthropicModel(undefined), maxTokens: 1 });\n  return r.success;\n}","typeGuard":null,"tryCatchPattern":"async function promptWithRetry(agentId: string, input: string, retries = 2): Promise<string> {\n  for (let attempt = 0; ; attempt++) {\n    try {\n      return await promptWasmAgent(agentId, input);\n    } catch (e) {\n      const msg = String(e);\n      const transient = /429|rate.?limit|timeout|econnreset|5\\d\\d/.test(msg);\n      if (!transient || attempt >= retries) throw e;\n      await new Promise(r => setTimeout(r, 500 * 2 ** attempt));\n    }\n  }\n}","preventionTips":["Confirm the env var is actually set in the process (print Boolean(process.env.ANTHROPIC_API_KEY)).","Validate model strings against the provider's current catalog before wiring the agent.","For offline/CI runs, unset all three keys so the echo stub engages instead of failing through the provider path.","Wrap multi-turn agent loops with retry+backoff for 429/5xx; the WASM runtime does not retry on its own."],"tags":["llm-provider","api","network","anthropic","openrouter","billing"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}