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

  1. Inspect the embedded message string to identify the failing layer (TCP reset, decode failure, protocol error)
  2. Re-establish the stream; for SSE-style endpoints resume from the last processed event id or offset if the protocol supports it
  3. Raise or disable idle/response timeouts on intermediaries (proxy, ALB, CDN) in the stream path
  4. 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

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


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/cfd45867e54a0a24. Report an issue: GitHub.