{"record":{"id":"9897381473c0d483","repo":"Hmbown/CodeWhale","slug":"provider-stream-error-error","errorCode":null,"errorMessage":"provider stream error: {error}","messagePattern":"provider stream error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/acp_server.rs","lineNumber":489,"sourceCode":"                            }\n                            StreamEvent::ContentBlockDelta {\n                                index,\n                                delta: Delta::InputJsonDelta { partial_json },\n                            } => {\n                                if let Some(acc) = pending_tool_uses.get_mut(&index) {\n                                    acc.buffer.push_str(&partial_json);\n                                }\n                            }\n                            StreamEvent::ContentBlockStop { index } => {\n                                if let Some(acc) = pending_tool_uses.remove(&index) {\n                                    tool_calls.push(acc.finalize());\n                                }\n                            }\n                            StreamEvent::MessageStop => {\n                                return Ok((PromptOutcome::Completed(accumulated), tool_calls));\n                            }\n                            StreamEvent::Error { error } => {\n                                return Err(anyhow!(\"provider stream error: {error}\"));\n                            }\n                            _ => {}\n                        }\n                    }\n                    Some(Err(err)) => return Err(err),\n                }\n            }\n            line = reader.next_line(), if reader_open => {\n                let line = match line? {\n                    Some(line) => line,\n                    // Input closed mid-turn: stop watching it, keep draining.\n                    None => {\n                        reader_open = false;\n                        continue;\n                    }\n                };\n                if line.trim().is_empty() {\n                    continue;","sourceCodeStart":471,"sourceCodeEnd":507,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/acp_server.rs#L471-L507","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet (outcome, calls) = run_turn(prompt).await?;\n// after\nlet mut attempt = 0;\nlet (outcome, calls) = loop {\n    match run_turn(prompt.clone()).await {\n        Ok(ok) => break ok,\n        Err(err) if err.to_string().starts_with(\"provider stream error\") && attempt < 3 => {\n            attempt += 1;\n            tokio::time::sleep(std::time::Duration::from_millis(500 * attempt)).await;\n        }\n        Err(err) => return Err(err),\n    }\n};","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let mut attempt = 0;\nloop {\n    match run_turn(prompt.clone()).await {\n        Ok(value) => break Ok(value),\n        Err(err) if err.to_string().starts_with(\"provider stream error\") && is_transient(&err) && attempt < 5 => {\n            attempt += 1;\n            tokio::time::sleep(backoff(attempt)).await;\n        }\n        Err(err) => break Err(err),\n    }\n}","preventionTips":["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"],"tags":["streaming","provider","llm","acp","network"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}