Hmbown/CodeWhale · error · anyhow::Error
SSE stream idle timeout after
Error message
SSE stream idle timeout after {idle_secs}s — no data received (bytes_received={bytes}, stream_age_ms={age}, ms_since_last_chunk={since}) What it means
The Responses API stream received no data at all from the server for longer than the configured idle timeout (`stream_idle_timeout`). The `select!` branch on the read future timed out, and the handler yields a diagnostic message (built by `idle_timeout_message`) that includes bytes received, stream age, and time since the last chunk, then aborts. This prevents hanging forever on a stalled connection.
Solutions
- Retry the request; stalls are often transient provider-side load
- Increase the stream idle timeout configuration if the model legitimately has long silent periods
- Check provider status / self-hosted server logs for the stall cause
- Confirm no proxy is buffering the SSE response (buffering proxies suppress chunk delivery)
Defensive patterns
Strategy: retry
Try / catch
match result {
Err(e) if e.to_string().contains("SSE stream idle timeout") => {
let diag = e.to_string(); // parse bytes_received/ms_since_last_chunk for diagnosis
retry_with_backoff();
}
other => other,
} Prevention
- Set the idle timeout above the endpoint's realistic time-to-first-token
- Ensure proxies forward SSE chunks without buffering
- Alert on provider stalls via the bytes_received/ms_since_last_chunk diagnostics
- Retry stalled streams automatically
When it happens
Trigger: The server accepts the request but sends no bytes within `stream_idle_timeout` seconds; the connection stalls mid-stream after some chunks (diagnostics show bytes_received > 0); a hung upstream behind a proxy that keeps the socket open but forwards nothing.
Common situations: Provider outages where the endpoint accepts connections but never streams; overloaded self-hosted inference servers queuing requests; misconfigured keepalive behind load balancers; extremely long time-to-first-token on large prompts.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- SSE stream idle timeout after
- SSE stream idle timeout after
- SSE stream idle timeout after
- SSE stream idle timeout after
- SSE stream idle timeout after
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/8daac956073b363a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/client/responses.rs:282
let mut bytes_received: usize = 0;
tokio::pin!(byte_stream);
while !done {
if !ended {
match tokio::time::timeout(stream_idle_timeout, byte_stream.next()).await {
Ok(Some(Ok(chunk))) => {
bytes_received += chunk.len();
last_chunk_at = std::time::Instant::now();
buffer.extend_from_slice(&chunk);
}
Ok(Some(Err(e))) => {
yield Err(anyhow::anyhow!("Stream read error: {e}"));
return;
}
Ok(None) => ended = true,
Err(_) => {
yield Err(anyhow::anyhow!(super::stream_entry::idle_timeout_message(
stream_idle_timeout,
bytes_received,
stream_start.elapsed(),
last_chunk_at.elapsed(),
)));
return;
}
}
}
// Process complete SSE lines, and the unterminated tail at stream end.
loop {
let line = match next_sse_line(&mut buffer, ended) {
Ok(Some(line)) => line,
Ok(None) => break,
Err(err) => {
yield Err(anyhow::anyhow!("{err}"));
return;View on GitHub (pinned to 433685b202)