Hmbown/CodeWhale · error · anyhow::Error

runtime SSE frame exceeded

Error message

runtime SSE frame exceeded {MAX_SSE_FRAME_BYTES} bytes without a frame delimiter

What it means

The app-server client reads a runtime SSE (Server-Sent Events) stream and accumulates chunks in a buffer. If the buffer grows past MAX_SSE_FRAME_BYTES without a complete frame delimiter appearing, this error is thrown to bound memory use, since a valid SSE frame can never legitimately be that large.

Solutions

  1. Inspect what the runtime is actually streaming (curl the SSE endpoint) to find the oversized frame's origin.
  2. Check for proxies/load balancers between client and runtime that strip SSE newlines or re-chunk the body.
  3. Verify the runtime version matches what the client expects; upgrade or downgrade so framing is standard SSE.
  4. If a legitimate event is too large, reduce the event payload size on the runtime side instead of raising the limit.
Defensive patterns

Strategy: validation

Try / catch

// Rust
match client.poll_events(...) {
    Err(e) if e.to_string().contains("SSE frame exceeded") => {
        log::error!("runtime streamed an oversized frame; check proxies/runtime version");
        // fail the turn rather than retrying blindly
    }
    other => other,
}

Prevention

When it happens

Trigger: Any call that consumes the runtime's SSE event stream (polling run events via HTTP chunked response) where a single event frame exceeds the configured byte limit before a newline/frame delimiter arrives.

Common situations: A malicious or broken runtime/server streaming a huge un-delimited body; a proxy mangling SSE framing so newlines are lost; a misconfigured runtime emitting extremely large payloads as one event.

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/2e30c097cbdf64e7. Report an issue: GitHub.

Appendix: source

Thrown at crates/app-server/src/lib.rs:1923

        since_seq: u64,
        mut transcript: Option<&mut TurnTranscript>,
    ) -> 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 73e0f67d83)