{"record":{"id":"5f4f380f9ab65425","repo":"Hmbown/CodeWhale","slug":"stream-read-error-e","errorCode":null,"errorMessage":"Stream read error: {e}","messagePattern":"Stream read error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/client/anthropic.rs","lineNumber":320,"sourceCode":"\n        let stream = async_stream::stream! {\n            use futures_util::StreamExt;\n\n            // Raw byte buffer: decode only COMPLETE lines so a multi-byte\n            // UTF-8 char (CJK/emoji) split across two network reads is never\n            // corrupted to U+FFFD. Line boundaries ('\\n') are ASCII and can\n            // never fall inside a multi-byte sequence. (Mirrors chat.rs.)\n            let mut buffer: Vec<u8> = Vec::new();\n            let stream_start = std::time::Instant::now();\n            let mut last_chunk_at = std::time::Instant::now();\n            let mut bytes_received: usize = 0;\n            tokio::pin!(byte_stream);\n\n            loop {\n                let chunk = match tokio::time::timeout(stream_idle_timeout, byte_stream.next()).await {\n                    Ok(Some(Ok(chunk))) => chunk,\n                    Ok(Some(Err(e))) => {\n                        yield Err(anyhow::anyhow!(\"Stream read error: {e}\"));\n                        return;\n                    }\n                    Ok(None) => break,\n                    Err(_) => {\n                        yield Err(anyhow::anyhow!(super::stream_entry::idle_timeout_message(\n                            stream_idle_timeout,\n                            bytes_received,\n                            stream_start.elapsed(),\n                            last_chunk_at.elapsed(),\n                        )));\n                        return;\n                    }\n                };\n\n                bytes_received += chunk.len();\n                last_chunk_at = std::time::Instant::now();\n                buffer.extend_from_slice(&chunk);\n","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/client/anthropic.rs#L302-L338","documentation":"Mid-stream transport failure in the Anthropic SSE reader: the underlying `bytes_stream()` yielded `Err(e)` (a reqwest/hyper I/O error) after the response headers and body had begun flowing. This is distinct from the idle timeout (no error, just silence) and from API-level stream errors (which arrive as well-formed `error` events); the raw transport error is wrapped verbatim.","triggerScenarios":"Connection reset by the server or an intermediary mid-generation; TLS session torn down; proxy/VPN dropping long-lived HTTP/2 streams; laptop sleep/resume during a long completion; NAT timeouts on idle-then-bursty streams.","commonSituations":"Corporate proxies or VPNs with aggressive idle connection reaping; HTTP/2 stream stalls behind middleboxes (the client already has an H2->HTTP/1.1 fallback at open time, but not mid-stream); flaky mobile/tethered networks during long generations.","solutions":["Retry the request — transient transport resets usually succeed on a fresh connection.","For repeated resets behind proxies/VPN, force HTTP/1.1 end-to-end (`CODEWHALE_FORCE_HTTP1=1`) to avoid H2 stream tearing.","Check the wrapped error text: `connection reset`/`unexpected EOF` points at intermediary boxes; TLS errors point at certificate/proxy interception.","On laptops, avoid suspending mid-generation; on unstable links prefer shorter outputs."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// In the stream consumer: a transport read error is terminal for this stream,\n// but the turn can be retried on a fresh connection.\nwhile let Some(ev) = stream.next().await {\n    match ev {\n        Ok(event) => handle(event),\n        Err(e) if e.to_string().starts_with(\"Stream read error\") => {\n            notify_user_partial_output();\n            return retry_turn_with_backoff().await; // fresh connection\n        }\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Retry turns whose stream dies mid-read — partial output plus a retry usually completes the work.","Set `CODEWHALE_FORCE_HTTP1=1` in proxy-heavy environments to avoid HTTP/2 stream resets.","Keep partial streamed text visible to the user so a retry is cheap (edit-from-partial rather than from scratch)."],"tags":["anthropic","streaming","network","sse","transport"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}