Hmbown/CodeWhale · error · anyhow::Error
SSE buffer exceeded bytes — aborting stream
Error message
SSE buffer exceeded {MAX_SSE_BUF} bytes — aborting stream What it means
A hard safety valve in the SSE parser: the accumulated unparsed byte buffer for the current SSE frame exceeded 10 MB (`MAX_SSE_BUF`), which can only happen if the stream is not producing newlines — i.e. malformed or malicious framing. The stream is aborted to prevent unbounded memory growth.
Solutions
- Check the response Content-Type is `text/event-stream` and the endpoint really speaks SSE
- Log the buffered bytes when this fires to identify what the server actually sent
- Fix the endpoint/proxy that is breaking SSE framing
- Verify TLS interception is not mangling the stream
Example fix
// before: endpoint returns JSON error page with 200
// after: check status/content-type and surface a clear upstream error before parsing
if !ct.starts_with("text/event-stream") { return Err(...); } Defensive patterns
Strategy: validation
Validate before calling
// before parsing, verify the response is actually SSE
fn response_is_sse(ct: &str, status: u16) -> bool {
status == 200 && ct.starts_with("text/event-stream")
} Try / catch
match result {
Err(e) if e.to_string().contains("SSE buffer exceeded") => {
// framing broken — capture buffer sample, do NOT retry blindly
dump_buffer_sample(); fail_with_upstream_context(e);
}
other => propagate(other),
} Prevention
- Check Content-Type is text/event-stream before entering the parse loop
- Reject non-SSE 200 bodies (HTML/JSON error pages) early
- Investigate TLS-intercepting proxies that mangle framing
- Keep the buffer guard in place — it protects against runaway servers
When it happens
Trigger: A server (or man-in-the-middle) streaming data without SSE line boundaries — non-SSE binary or HTML dumped into the connection, a broken Content-Type, or a runaway server bug never emitting `\n`.
Common situations: Pointing the client at an endpoint that returns an HTML error page or JSON body on a 200 without SSE framing; a proxy stripping or corrupting newlines; a compromised/misconfigured endpoint flooding data.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- {err}
- MCP SSE frame exceeded
- PythonRuntime::with_state_path is deprecated — use…
- runtime SSE frame exceeded
- Chat Completions stream closed before [DONE] or…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/27d3f00ccc350dfc.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/client/chat.rs:1523
stream_start.elapsed().as_millis(),
bytes_received,
last_event_at.elapsed().as_millis(),
response_headers,
));
yield Err(anyhow::anyhow!("Stream read error: {e}"));
break;
}
};
bytes_received = bytes_received.saturating_add(chunk.len());
last_event_at = std::time::Instant::now();
byte_buf.extend_from_slice(&chunk);
// Guard against unbounded buffer growth (e.g., malformed stream without newlines)
const MAX_SSE_BUF: usize = 10 * 1024 * 1024; // 10 MB
if byte_buf.len() > MAX_SSE_BUF {
stream_failed = true;
yield Err(anyhow::anyhow!("SSE buffer exceeded {MAX_SSE_BUF} bytes — aborting stream"));
break;
}
if byte_buf.len() > SSE_BACKPRESSURE_HIGH_WATERMARK {
tokio::time::sleep(Duration::from_millis(SSE_BACKPRESSURE_SLEEP_MS)).await;
}
// Process complete SSE lines from the buffer. Decode only after
// a `\n` so an HTTP/2 DATA split mid-character cannot become
// U+FFFD; genuine invalid bytes fail closed.
let mut lines_processed = 0usize;
loop {
let line = match take_sse_line(&mut byte_buf) {
Ok(Some(line)) => line,
Ok(None) => break,
Err(err) => {
decode_failed = true;
stream_failed = true;View on GitHub (pinned to 73e0f67d83)