Hmbown/CodeWhale · error

MCP SSE frame exceeded

Error message

MCP SSE frame exceeded {} bytes without a separator — aborting

What it means

SSE frames are buffered until a separator (blank line delimiting an event) is found. If the buffer grows past `MAX_SSE_FRAME_BYTES` without any separator, the stream is presumed malicious or broken and aborted rather than consuming unbounded memory.

Solutions

  1. Confirm the URL returns actual `text/event-stream` SSE data with proper blank-line separators
  2. Check for proxies/CDNs buffering or mangling the stream; connect directly or fix the proxy
  3. If a single legitimate event is truly huge, raise `MAX_SSE_FRAME_BYTES` (server-side fix preferred)
  4. Reduce the event payload size on the server

Example fix

// before
// client pointed at a plain JSON endpoint
{"url": "https://host/api/events.json"}
// after
{"url": "https://host/sse"} // real text/event-stream endpoint
Defensive patterns

Strategy: try-catch

Validate before calling

// verify endpoint speaks SSE
let ct = reqwest::get(url).await?.headers().get("content-type")
    .and_then(|v| v.to_str().ok()).unwrap_or("");
if !ct.starts_with("text/event-stream") {
    return Err(anyhow!("not an SSE endpoint"));
}

Try / catch

match run_sse_loop(...).await {
    Err(e) if e.to_string().contains("without a separator") => {
        error!("endpoint is not emitting valid SSE framing; check URL/proxy");
    }
    other => other?,
}

Prevention

When it happens

Trigger: A server (or middlebox) sends a continuous byte stream with no SSE event separator, or a single event larger than the frame cap; corrupted/garbage responses not actually speaking SSE.

Common situations: Pointing the client at a plain HTTP streaming endpoint (non-SSE) or a binary stream; a buggy proxy mangling framing; an enormous single event from a misbehaving server.

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


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/e575ee14bdf19ee5. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/mcp/sse.rs:158

                tracing::debug!("SSE loop cancelled");
                break;
            }
            let item = tokio::select! {
                _ = cancel_token.cancelled() => {
                    tracing::debug!("SSE loop shutting down");
                    break;
                }
                item = stream.next() => {
                    match item {
                        Some(i) => i,
                        None => break,
                    }
                }
            };
            let chunk = item?;
            buffer.extend_from_slice(&chunk);
            if buffer.len() > MAX_SSE_FRAME_BYTES {
                anyhow::bail!(
                    "MCP SSE frame exceeded {} bytes without a separator — aborting",
                    MAX_SSE_FRAME_BYTES
                );
            }

            while let Some((pos, separator_len)) = find_sse_event_separator_bytes(&buffer) {
                // Complete block: decoding cannot split a multi-byte char.
                let event_block = String::from_utf8_lossy(&buffer[..pos]).into_owned();
                buffer.drain(..pos + separator_len);

                let mut event_type = "message";
                let mut data = String::new();

                for line in event_block.lines() {
                    if let Some(value) = sse_field_value(line, "event:") {
                        event_type = value;
                    } else if let Some(value) = sse_field_value(line, "data:") {
                        if !data.is_empty() {

View on GitHub (pinned to 73e0f67d83)