ruvnet/ruflo · error
provider call failed
Error message
provider call failed
What it means
Thrown inside the JsModelProvider callback wired into a WASM agent: callAnthropicMessages() returned success=false, and when its error field was empty this generic message is used. The backing LLM HTTP call failed before producing output — auth, network, model, or quota — so the WASM agent's conversation turn cannot proceed.
Source
Thrown at v3/@claude-flow/cli/src/ruvector/agent-wasm.ts:172
* Called once at agent-creation time; the provider stays attached for the
* agent's lifetime. No-op (returns false) when no provider keys are
* configured so the echo-fallback path below is preserved for keyless
* environments.
*/
async function attachJsModelProvider(agent: any, config: WasmAgentConfig): Promise<boolean> {
const hasAny = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENROUTER_API_KEY || process.env.OLLAMA_API_KEY);
if (!hasAny) return false;
const mod = await import('@ruvector/rvagent-wasm');
const { callAnthropicMessages, resolveAnthropicModel } = await import('../mcp-tools/agent-execute-core.js');
const model = resolveAnthropicModel(config.model);
const systemPrompt = config.instructions || 'You are a helpful coding assistant running in a Ruflo WASM agent sandbox.';
const provider = new mod.JsModelProvider(async (messagesJson: string) => {
const messages: Array<{ role: string; content: string }> = JSON.parse(messagesJson);
const lastUser = [...messages].reverse().find(m => m.role === 'user');
const prompt = lastUser?.content ?? messagesJson;
const result = await callAnthropicMessages({ prompt, systemPrompt, model, maxTokens: 2048 });
if (!result.success) throw new Error(result.error ?? 'provider call failed');
return JSON.stringify({ role: 'assistant', content: result.output ?? '' });
});
agent.set_model_provider(provider);
return true;
}
/**
* Send a prompt to a WASM agent.
*
* ADR-129 P1: JsModelProvider is now wired at creation time so the WASM
* agent's internal conversation loop (multi-turn state, turn_count,
* stop conditions) runs against a real LLM. The echo-stub detection
* block is kept as a fallback for keyless environments (CI, sandboxed
* test runners) — behaviour is identical to the pre-P1 path when no
* provider key is set.
*
* Billing note: every wasm_agent_prompt call with a provider key
* configured makes a billable LLM call. Use a keyless environment toView on GitHub (pinned to fa13ee4ad6)
Solutions
- Verify the key with a minimal authenticated request to the provider and expect 200/400 rather than 401
- Check the resolved model: log resolveAnthropicModel(config.model) and confirm it is a currently valid model id
- Retry after rate-limit windows and confirm remaining quota in the provider console
- Run keyless (unset ANTHROPIC_API_KEY / OPENROUTER_API_KEY / OLLAMA_API_KEY) when the echo stub is sufficient — it makes no provider calls
Example fix
# before export ANTHROPIC_API_KEY=sk-ant-expired # promptWasmAgent → 'provider call failed' # after export ANTHROPIC_API_KEY=<valid key> # or, for cost-free sandboxing: unset ANTHROPIC_API_KEY OPENROUTER_API_KEY OLLAMA_API_KEY # echo stub
Defensive patterns
Strategy: fallback
Validate before calling
const hasKey = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENROUTER_API_KEY || process.env.OLLAMA_API_KEY);
if (!hasKey) {
// keyless path: agents run on the echo stub, no provider calls are made
console.log('no provider key set — echo stub in effect');
} Type guard
const isProviderCallFailure = (e: unknown): e is Error => e instanceof Error && /provider call failed/.test(e.message);
Try / catch
try {
return await promptWasmAgent(agentId, input);
} catch (e) {
if (isProviderCallFailure(e)) {
await ensureProviderReachable(); // key/network sanity check
return `echo: ${input}`; // fall back to keyless echo-stub behaviour
}
throw e;
} Prevention
- Validate API keys at startup with a cheap provider call
- Unset provider keys in CI when the echo stub is intended
- Watch provider status pages for rate limits during bulk runs
- Log provider response bodies — this generic message means the underlying error field was empty
When it happens
Trigger: ANTHROPIC_API_KEY (or the resolved provider key) set but invalid/expired (401); rate limit or quota exhausted (429); network egress blocked from the sandbox; resolveAnthropicModel(config.model) producing a model id the API rejects.
Common situations: Rotated API keys with stale env vars in long-lived shells; CI runners without egress to the provider API; retired model names pinned in config; token quota exhausted mid-run.
Related errors
- HTTP transport failed: ${firstError instanceof Error ? first
- Failed to fetch ${baseURL}/models: ${response.status} ${resp
- Resolved IP for ${hostname} is internal (${address})
- Could not reach the Cognitum auth service. ruflo core functi
- Failed to fetch manifest from ${url}: ${res.status} ${res.st
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/c15304e57754a571.
Report an issue: GitHub.