Hmbown/CodeWhale · error

Stream read error: {e}

Error message

Stream read error: {e}

What it means

While reading the Responses API SSE body, reqwest returned an error mid-stream (the `Ok(Some(Err(e)))` arm). The client yields this error and returns, ending the stream; the adjacent timeout arm handles idle expiry with the shared `idle_timeout_message`.

Source

Thrown at crates/tui/src/client/responses.rs:239

            let mut saw_tool_call = false;
            let mut usage_data: Option<Usage> = None;
            // Raw byte buffer: decode only COMPLETE lines so a multi-byte
            // UTF-8 char split across two network reads is never corrupted
            // to U+FFFD (line boundaries are ASCII). Mirrors chat.rs.
            let mut buffer: Vec<u8> = Vec::new();
            let mut done = false;
            let mut content_block_counter: u32 = 0;
            let stream_start = std::time::Instant::now();
            let mut last_chunk_at = std::time::Instant::now();
            let mut bytes_received: usize = 0;

            tokio::pin!(byte_stream);

            while !done {
                let chunk = match tokio::time::timeout(stream_idle_timeout, byte_stream.next()).await {
                    Ok(Some(Ok(chunk))) => chunk,
                    Ok(Some(Err(e))) => {
                        yield Err(anyhow::anyhow!("Stream read error: {e}"));
                        return;
                    }
                    Ok(None) => break,
                    Err(_) => {
                        yield Err(anyhow::anyhow!(super::stream_entry::idle_timeout_message(
                            stream_idle_timeout,
                            bytes_received,
                            stream_start.elapsed(),
                            last_chunk_at.elapsed(),
                        )));
                        return;
                    }
                };

                bytes_received += chunk.len();
                last_chunk_at = std::time::Instant::now();
                buffer.extend_from_slice(&chunk);

View on GitHub (pinned to 8880682c63)

Solutions

  1. Retry with backoff — most mid-body transport errors are transient
  2. If it recurs at a consistent point, check for intermediary time/size limits and bypass them
  3. On persistent failure, test the same request non-streaming to isolate the transport from the request
Defensive patterns

Strategy: retry

Type guard

fn is_stream_read_error(err: &anyhow::Error) -> bool {
    err.to_string().starts_with("Stream read error")
}

Try / catch

match stream.next().await {
    Some(Err(e)) if is_stream_read_error(&e) => {
        backoff().await;
        stream = client.reopen_responses_stream(request).await?;
    }
    Some(Err(e)) => return Err(e),
    other => { /* forward */ }
}

Prevention

When it happens

Trigger: Connection reset or HTTP/2 RST_STREAM during the Responses API stream; decompression failure; proxy cutting the long-lived connection.

Common situations: Long streaming completions through proxies; unstable networks; server-side connection recycling.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/2ec0448430871da0. Report an issue: GitHub.