{"record":{"id":"2e30c097cbdf64e7","repo":"Hmbown/CodeWhale","slug":"runtime-sse-frame-exceeded-max-sse-frame-bytes-b","errorCode":null,"errorMessage":"runtime SSE frame exceeded {MAX_SSE_FRAME_BYTES} bytes without a frame delimiter","messagePattern":"runtime SSE frame exceeded (.+?) bytes without a frame delimiter","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/app-server/src/lib.rs","lineNumber":1282,"sourceCode":"        writer: &mut W,\n        since_seq: u64,\n    ) -> Result<(u64, TurnTerminalStatus, Option<String>)> {\n        let mut response = self\n            .authed(self.client.get(format!(\n                \"{}/v1/threads/{thread_id}/events?since_seq={since_seq}\",\n                self.base_url\n            )))\n            .send()\n            .await?\n            .error_for_status()?;\n\n        let mut buffer = Vec::new();\n        let mut last_seq = since_seq;\n\n        while let Some(chunk) = response.chunk().await? {\n            buffer.extend_from_slice(&chunk);\n            if buffer.len() > MAX_SSE_FRAME_BYTES {\n                bail!(\n                    \"runtime SSE frame exceeded {MAX_SSE_FRAME_BYTES} bytes without a frame delimiter\"\n                );\n            }\n            while let Some(frame_bytes) = take_sse_frame(&mut buffer) {\n                let Some((event_name, frame_data)) = parse_sse_frame(&frame_bytes) else {\n                    continue;\n                };\n                let envelope: Value = serde_json::from_str(&frame_data)\n                    .with_context(|| format!(\"invalid SSE json for {event_name}: {frame_data}\"))?;\n                if let Some(seq) = envelope.get(\"seq\").and_then(Value::as_u64) {\n                    last_seq = last_seq.max(seq);\n                }\n                if envelope.get(\"turn_id\").and_then(Value::as_str) != Some(turn_id) {\n                    continue;\n                }\n                let payload = envelope.get(\"payload\").cloned().unwrap_or(Value::Null);\n                match event_name.as_str() {\n                    \"item.delta\" => {","sourceCodeStart":1264,"sourceCodeEnd":1300,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/app-server/src/lib.rs#L1264-L1300","documentation":"While streaming runtime events over SSE, the app-server buffers chunks until a frame delimiter is found (take_sse_frame). If the buffer grows past MAX_SSE_FRAME_BYTES (16 MiB in this crate) without any frame boundary, it aborts rather than allocate unbounded memory. This is a defensive cap against a misbehaving runtime or a non-SSE endpoint.","triggerScenarios":"Streaming /events (drain_events) from a URL that returns a huge single JSON blob instead of newline-delimited SSE frames; a runtime that emits one enormous event (>16 MiB, e.g. a giant serialized document in one frame); a proxy that strips/buffers SSE delimiters.","commonSituations":"Pointing the SSE stream_url at a plain JSON REST endpoint, a runtime version that serializes an oversized payload into a single event, or an intermediary (debugging proxy, load balancer) that mangles the event framing.","solutions":["Confirm the stream URL is the runtime's SSE endpoint, not a JSON API that returns one big body.","Reproduce with curl -N against the same URL and check whether events are actually frame-delimited (blank-line separated).","If a legitimate single event exceeds 16 MiB, shrink the payload on the runtime side (paginate, truncate large fields) rather than raising the cap.","Remove any proxy between app-server and runtime that rewrites or buffers the stream."],"exampleFix":"# before: stream pointed at a JSON endpoint returning one huge body\nstream_url = format!(\"{base_url}/threads\")\n\n# after: use the runtime's SSE event endpoint\nstream_url = format!(\"{base_url}/events?since_seq={seq}\")","handlingStrategy":"validation","validationCode":"// Confirm the endpoint speaks SSE before subscribing\nlet resp = client.get(&stream_url).send().await?;\nif !resp.headers().get(CONTENT_TYPE).and_then(|v| v.to_str().ok())\n    .map(|v| v.contains(\"text/event-stream\")).unwrap_or(false) {\n    anyhow::bail!(\"not an SSE endpoint; refusing to stream\");\n}","typeGuard":null,"tryCatchPattern":"match server.drain_events(since).await {\n    Ok(events) => events,\n    Err(e) if e.to_string().contains(\"SSE frame exceeded\") => {\n        // do NOT retry unchanged: the endpoint/payload is wrong by construction\n        anyhow::bail!(\"runtime stream misconfigured: {e}\")\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Always use the runtime-provided SSE stream URL, never a JSON REST path.","Keep single SSE events small on the producer side (paginate large documents).","Ensure no intermediary buffers or rewrites the event stream."],"tags":["sse","streaming","memory-limit","app-server"],"backgroundTag":"sse-frame-too-large","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}