Hmbown/CodeWhale · error · anyhow::Error
runtime SSE frame exceeded {MAX_SSE_FRAME_BYTES} bytes witho
Error message
runtime SSE frame exceeded {MAX_SSE_FRAME_BYTES} bytes without a frame delimiter What it means
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.
Source
Thrown at crates/app-server/src/lib.rs:1282
writer: &mut W,
since_seq: u64,
) -> Result<(u64, TurnTerminalStatus, Option<String>)> {
let mut response = self
.authed(self.client.get(format!(
"{}/v1/threads/{thread_id}/events?since_seq={since_seq}",
self.base_url
)))
.send()
.await?
.error_for_status()?;
let mut buffer = Vec::new();
let mut last_seq = since_seq;
while let Some(chunk) = response.chunk().await? {
buffer.extend_from_slice(&chunk);
if buffer.len() > MAX_SSE_FRAME_BYTES {
bail!(
"runtime SSE frame exceeded {MAX_SSE_FRAME_BYTES} bytes without a frame delimiter"
);
}
while let Some(frame_bytes) = take_sse_frame(&mut buffer) {
let Some((event_name, frame_data)) = parse_sse_frame(&frame_bytes) else {
continue;
};
let envelope: Value = serde_json::from_str(&frame_data)
.with_context(|| format!("invalid SSE json for {event_name}: {frame_data}"))?;
if let Some(seq) = envelope.get("seq").and_then(Value::as_u64) {
last_seq = last_seq.max(seq);
}
if envelope.get("turn_id").and_then(Value::as_str) != Some(turn_id) {
continue;
}
let payload = envelope.get("payload").cloned().unwrap_or(Value::Null);
match event_name.as_str() {
"item.delta" => {View on GitHub (pinned to 0c42157ee5)
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.
Example fix
# before: stream pointed at a JSON endpoint returning one huge body
stream_url = format!("{base_url}/threads")
# after: use the runtime's SSE event endpoint
stream_url = format!("{base_url}/events?since_seq={seq}") Defensive patterns
Strategy: validation
Validate before calling
// Confirm the endpoint speaks SSE before subscribing
let resp = client.get(&stream_url).send().await?;
if !resp.headers().get(CONTENT_TYPE).and_then(|v| v.to_str().ok())
.map(|v| v.contains("text/event-stream")).unwrap_or(false) {
anyhow::bail!("not an SSE endpoint; refusing to stream");
} Try / catch
match server.drain_events(since).await {
Ok(events) => events,
Err(e) if e.to_string().contains("SSE frame exceeded") => {
// do NOT retry unchanged: the endpoint/payload is wrong by construction
anyhow::bail!("runtime stream misconfigured: {e}")
}
Err(e) => Err(e),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- runtime event stream ended before turn.completed
- PythonRuntime::with_state_path is deprecated — use PythonRun
- SSE stream request failed: HTTP {status}: {error_text}
- unrecognized SSE event: {e}
- SSE stream idle timeout after {}s — no data received (bytes_
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/2e30c097cbdf64e7.
Report an issue: GitHub.