Hmbown/CodeWhale · error

SSE stream idle timeout after {}s — no data received (bytes_

Error message

SSE stream idle timeout after {}s — no data received (bytes_received={}, stream_age_ms={}, ms_since_last_chunk={})

What it means

The tokio timeout wrapping `byte_stream.next()` elapsed with no chunk received inside the idle window, so the client yields this error with diagnostics: idle seconds, total bytes received, stream age, and time since the last chunk. These fields distinguish a stream that never produced a byte (server never started) from one that stalled mid-generation.

Source

Thrown at crates/tui/src/client/chat.rs:1301

            // Telemetry for #103 stream-decode diagnostics: bytes received
            // since the start of this stream and last successful event time.
            // Surfaces in the error log when reqwest yields a chunk error so
            // we can tell HTTP/2 RST_STREAM from chunk-decode-failure from
            // gzip-corruption when investigating a flaky session.
            let stream_start = std::time::Instant::now();
            let mut last_event_at = std::time::Instant::now();
            let mut bytes_received: usize = 0;
            // Set when a `[DONE]` sentinel was seen, so the post-loop flush does
            // not re-process trailing post-DONE bytes.
            let mut saw_done = false;

            'stream: loop {
                let chunk_result = match tokio_timeout(idle, byte_stream.next()).await {
                    Ok(Some(result)) => result,
                    Ok(None) => break, // Stream ended normally
                    Err(_elapsed) => {
                        yield Err(anyhow::anyhow!(stream_idle_timeout_message(
                            idle,
                            bytes_received,
                            stream_start.elapsed(),
                            last_event_at.elapsed(),
                        )));
                        break;
                    }
                };
                let chunk = match chunk_result {
                    Ok(bytes) => bytes,
                    Err(e) => {
                        // Walk the error source chain so reqwest's underlying
                        // hyper / h2 / io error is visible — without this the
                        // outer "error decoding response body" message tells
                        // us nothing about WHY the stream died.
                        let mut error_chain = format!("{e}");
                        let mut current: Option<&(dyn std::error::Error + 'static)> =
                            std::error::Error::source(&e);

View on GitHub (pinned to 8880682c63)

Solutions

  1. Retry the request — a silent stall is usually transient
  2. Increase the stream idle timeout so it exceeds the model's expected time-to-first-token (reasoning models can think for tens of seconds silently)
  3. If bytes_received=0 consistently, the server is not starting the response — verify the endpoint and request
  4. Check for intermediaries (proxies, VPNs) that kill idle connections and bypass them
Defensive patterns

Strategy: retry

Type guard

fn is_stream_idle_timeout(err: &anyhow::Error) -> bool {
    err.to_string().starts_with("SSE stream idle timeout")
}

Try / catch

match stream.next().await {
    Some(Err(e)) if is_stream_idle_timeout(&e) => {
        backoff().await;
        stream = client.reopen_stream(request).await?; // idempotent re-request
    }
    Some(Err(e)) => return Err(e),
    other => { /* forward */ }
}

Prevention

When it happens

Trigger: Reasoning models that think for a long time before the first token, exceeding the idle window; a stalled proxy or network path; provider-side hang after 200 OK; idle timeout configured smaller than the provider's time-to-first-token.

Common situations: High-latency or mobile networks; deep-reasoning requests with long silent thinking phases; corporate proxies that idle-kill quiet connections.

Understand the failure class

Related errors


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