Kuberwastaken/claurst · error · anyhow::Error

failed reading SSE stream

Error message

failed reading SSE stream: {}

What it means

While consuming a server-sent-events stream from an MCP server, the reqwest bytes_stream yielded an Err. The library wraps the underlying transport error (hyper/reqwest/io) in this message, so the displayed cause is the raw stream read failure.

Solutions

  1. Retry the request; add reconnect/backoff logic for long SSE streams
  2. Check server and reverse-proxy idle timeouts and raise them
  3. Verify network/TLS stability between client and MCP server
  4. Inspect the wrapped cause (`{}` payload) for the concrete hyper/io error
Defensive patterns

Strategy: retry

Try / catch

match read_sse().await {
    Err(e) if e.to_string().contains("failed reading SSE stream") => {
        backoff_retry(|| read_sse(), 3).await?
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling an MCP server over SSE and the TCP connection breaks mid-stream: server crash, network drop, proxy idle timeout, TLS termination, or reqwest body read error.

Common situations: Long-lived SSE connections killed by load balancer idle timeouts; server restarted during a request; flaky network or VPN disconnect; TLS certificate issues surfacing mid-stream.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/bb66f5a52599885e. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/mcp/src/lib.rs:509

            "data" => data_lines.push(value.to_string()),
            _ => {}
        }
    }

    pub(crate) async fn process_sse_response<F>(
        response: reqwest::Response,
        mut on_event: F,
    ) -> anyhow::Result<()>
    where
        F: FnMut(Option<&str>, &str) -> anyhow::Result<()>,
    {
        let mut stream = response.bytes_stream();
        let mut buffer = String::new();
        let mut event_name: Option<String> = None;
        let mut data_lines: Vec<String> = Vec::new();

        while let Some(chunk) = stream.next().await {
            let chunk = chunk.map_err(|e| anyhow::anyhow!("failed reading SSE stream: {}", e))?;
            buffer.push_str(&String::from_utf8_lossy(&chunk));

            while let Some(pos) = buffer.find('\n') {
                let mut line: String = buffer.drain(..=pos).collect();
                if line.ends_with('\n') {
                    line.pop();
                }
                if line.ends_with('\r') {
                    line.pop();
                }
                if line.is_empty() {
                    dispatch_sse_event(&mut event_name, &mut data_lines, &mut on_event)?;
                } else {
                    process_sse_line(&line, &mut event_name, &mut data_lines);
                }
            }
        }

View on GitHub (pinned to b0637c97ec)