{"record":{"id":"e575ee14bdf19ee5","repo":"Hmbown/CodeWhale","slug":"mcp-sse-frame-exceeded-bytes-without-a-separato","errorCode":null,"errorMessage":"MCP SSE frame exceeded {} bytes without a separator — aborting","messagePattern":"MCP SSE frame exceeded (.+?) bytes without a separator — aborting","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp/sse.rs","lineNumber":158,"sourceCode":"                tracing::debug!(\"SSE loop cancelled\");\n                break;\n            }\n            let item = tokio::select! {\n                _ = cancel_token.cancelled() => {\n                    tracing::debug!(\"SSE loop shutting down\");\n                    break;\n                }\n                item = stream.next() => {\n                    match item {\n                        Some(i) => i,\n                        None => break,\n                    }\n                }\n            };\n            let chunk = item?;\n            buffer.extend_from_slice(&chunk);\n            if buffer.len() > MAX_SSE_FRAME_BYTES {\n                anyhow::bail!(\n                    \"MCP SSE frame exceeded {} bytes without a separator — aborting\",\n                    MAX_SSE_FRAME_BYTES\n                );\n            }\n\n            while let Some((pos, separator_len)) = find_sse_event_separator_bytes(&buffer) {\n                // Complete block: decoding cannot split a multi-byte char.\n                let event_block = String::from_utf8_lossy(&buffer[..pos]).into_owned();\n                buffer.drain(..pos + separator_len);\n\n                let mut event_type = \"message\";\n                let mut data = String::new();\n\n                for line in event_block.lines() {\n                    if let Some(value) = sse_field_value(line, \"event:\") {\n                        event_type = value;\n                    } else if let Some(value) = sse_field_value(line, \"data:\") {\n                        if !data.is_empty() {","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp/sse.rs#L140-L176","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Confirm the URL returns actual `text/event-stream` SSE data with proper blank-line separators","Check for proxies/CDNs buffering or mangling the stream; connect directly or fix the proxy","If a single legitimate event is truly huge, raise `MAX_SSE_FRAME_BYTES` (server-side fix preferred)","Reduce the event payload size on the server"],"exampleFix":"// before\n// client pointed at a plain JSON endpoint\n{\"url\": \"https://host/api/events.json\"}\n// after\n{\"url\": \"https://host/sse\"} // real text/event-stream endpoint","handlingStrategy":"try-catch","validationCode":"// verify endpoint speaks SSE\nlet ct = reqwest::get(url).await?.headers().get(\"content-type\")\n    .and_then(|v| v.to_str().ok()).unwrap_or(\"\");\nif !ct.starts_with(\"text/event-stream\") {\n    return Err(anyhow!(\"not an SSE endpoint\"));\n}","typeGuard":null,"tryCatchPattern":"match run_sse_loop(...).await {\n    Err(e) if e.to_string().contains(\"without a separator\") => {\n        error!(\"endpoint is not emitting valid SSE framing; check URL/proxy\");\n    }\n    other => other?,\n}","preventionTips":["Point only at real text/event-stream endpoints","Avoid proxies that buffer or rewrite streaming responses","Keep individual SSE events under the client frame cap"],"tags":["mcp","sse","protocol","memory"],"backgroundTag":"payload-too-large","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}