Hmbown/CodeWhale · error
provider stream error: {error}
Error message
provider stream error: {error} What it means
The ACP server forwards a model-provider streaming turn as SSE events; when the provider emits StreamEvent::Error before MessageStop, the partially accumulated turn is discarded and the provider's message is wrapped in this anyhow error. The text after the colon is the provider's own error description (rate limit, overloaded, auth, context length, upstream termination).
Source
Thrown at crates/tui/src/acp_server.rs:489
}
StreamEvent::ContentBlockDelta {
index,
delta: Delta::InputJsonDelta { partial_json },
} => {
if let Some(acc) = pending_tool_uses.get_mut(&index) {
acc.buffer.push_str(&partial_json);
}
}
StreamEvent::ContentBlockStop { index } => {
if let Some(acc) = pending_tool_uses.remove(&index) {
tool_calls.push(acc.finalize());
}
}
StreamEvent::MessageStop => {
return Ok((PromptOutcome::Completed(accumulated), tool_calls));
}
StreamEvent::Error { error } => {
return Err(anyhow!("provider stream error: {error}"));
}
_ => {}
}
}
Some(Err(err)) => return Err(err),
}
}
line = reader.next_line(), if reader_open => {
let line = match line? {
Some(line) => line,
// Input closed mid-turn: stop watching it, keep draining.
None => {
reader_open = false;
continue;
}
};
if line.trim().is_empty() {
continue;View on GitHub (pinned to 8880682c63)
Solutions
- Read the {error} text: provider codes (429/529/401/context-length) each dictate a different fix
- Retry the turn with exponential backoff for transient limits or overload
- Trim the conversation or reduce prompt size if a context-window error is reported
- Verify API key validity and the provider status page for auth or 5xx errors
Example fix
// before
let (outcome, calls) = run_turn(prompt).await?;
// after
let mut attempt = 0;
let (outcome, calls) = loop {
match run_turn(prompt.clone()).await {
Ok(ok) => break ok,
Err(err) if err.to_string().starts_with("provider stream error") && attempt < 3 => {
attempt += 1;
tokio::time::sleep(std::time::Duration::from_millis(500 * attempt)).await;
}
Err(err) => return Err(err),
}
}; Defensive patterns
Strategy: retry
Try / catch
let mut attempt = 0;
loop {
match run_turn(prompt.clone()).await {
Ok(value) => break Ok(value),
Err(err) if err.to_string().starts_with("provider stream error") && is_transient(&err) && attempt < 5 => {
attempt += 1;
tokio::time::sleep(backoff(attempt)).await;
}
Err(err) => break Err(err),
}
} Prevention
- Classify the provider message first: retry 429/529 with backoff, but fail fast on auth or context-length errors
- Keep conversations under the model context budget so mid-stream context errors cannot occur
- Rotate credentials before expiry to avoid mid-stream auth failures
- Make turn requests idempotent so a retried turn never duplicates side effects
When it happens
Trigger: Any streaming prompt/session/continue turn where the provider sends an error event mid-message: 429 rate limit, 529 overloaded, request exceeding the model context window, revoked/expired API key, or the upstream connection dropping mid-stream.
Common situations: Bursty automation hitting provider rate limits; very long conversations that grew past the model context; expired or rotated credentials; provider-side incidents; flaky egress proxies terminating SSE connections.
Related errors
- DeepSeek ${res.status}: ${text}
- Antigravity cloud-code is stream-only; blocking create_messa
- Stream read error: {e}
- SSE stream idle timeout after {}s — no data received (bytes_
- Stream read error: {e}
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/9897381473c0d483.
Report an issue: GitHub.