openai/codex · error · StreamError
stream failed: {0}
Error message
stream failed: {0} What it means
StreamError::Stream(String) is defined in the shared HTTP transport's error module for consumers of response byte/SSE streams (exported at lib.rs:32). It reports that consuming the stream failed after a successful start, carrying the underlying failure's message as a plain string — typically a connection reset or protocol/decode failure mid-body, as opposed to StreamError::Timeout which means no data arrived within the deadline.
Source
Thrown at codex-rs/http-client/src/error.rs:31
url: Option<String>,
headers: Option<HeaderMap>,
body: Option<String>,
},
#[error("retry limit reached")]
RetryLimit,
#[error("timeout")]
Timeout,
#[error("connection failed: {0}")]
Connection(#[source] HttpError),
#[error("network error: {0}")]
Network(String),
#[error("request build error: {0}")]
Build(String),
}
#[derive(Debug, Error)]
pub enum StreamError {
#[error("stream failed: {0}")]
Stream(String),
#[error("timeout")]
Timeout,
}
View on GitHub (pinned to 339751715c)
Solutions
- Inspect the embedded message string to identify the failing layer (TCP reset, decode failure, protocol error)
- Re-establish the stream; for SSE-style endpoints resume from the last processed event id or offset if the protocol supports it
- Raise or disable idle/response timeouts on intermediaries (proxy, ALB, CDN) in the stream path
- Bypass the proxy for the streaming host to isolate which hop terminates the response
Example fix
// before
while let Some(chunk) = stream.next().await { out.extend(&chunk?); } // StreamError::Stream aborts the consumer
// after: supervised consume with resume
loop {
match consume_from(&mut stream, &mut checkpoint).await {
Err(StreamError::Stream(msg)) => { log::warn!("stream reset: {msg}"); stream = reconnect(checkpoint).await?; }
other => break other?,
}
} Defensive patterns
Strategy: retry
Type guard
fn is_stream_failure(e: &StreamError) -> bool {
matches!(e, StreamError::Stream(_))
} Try / catch
match stream.next().await {
Some(Err(StreamError::Stream(msg))) => {
tracing::warn!(%msg, "stream interrupted, reconnecting");
stream = resume_from(checkpoint).await?;
}
Some(Err(StreamError::Timeout)) => { /* extend deadline, poll again */ }
other => return other.transpose(),
} Prevention
- Checkpoint consumed offsets so an interrupted stream can resume instead of restarting
- Add application-level heartbeats or subscribe to server keep-alives to distinguish idle from dead
- Bound retry storms with jitter and a maximum reconnect count
- Raise idle timeouts on proxies and load balancers in the streaming path
When it happens
Trigger: Polling a response stream where the underlying read fails after headers were received: the peer or a proxy resets the connection mid-body, chunked transfer framing is malformed, or a body decoder errors while yielding bytes.
Common situations: Long-lived SSE/model-token streams dropped by middlebox idle timeouts, proxies with response-size or buffering limits that terminate long responses, unstable mobile or VPN links interrupting downloads.
Related errors
- Luna Responses WebSocket connection timed out
- timeout
- connection failed: {0}
- network error: {0}
- too many redirects
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/cfd45867e54a0a24.
Report an issue: GitHub.