Hmbown/CodeWhale · error · anyhow::Error
MCP response body exceeds {max_bytes} bytes — aborting
Error message
MCP response body exceeds {max_bytes} bytes — aborting What it means
The streamed body crossed max_bytes (MAX_MCP_RESPONSE_BYTES = 16 MiB) while read_body_capped() iterated the byte stream — the response was chunked or declared no Content-Length, so the upfront length guard could not catch it. Same OOM protection as the Content-Length guard, applied to the actual stream.
Source
Thrown at crates/tui/src/mcp/streamable_http.rs:181
}
/// Read a response body through the byte stream, failing as soon as it
/// exceeds `max_bytes`. This bounds chunked and missing-Content-Length
/// responses exactly like declared ones (the declared-length fast path in
/// `send` only covers servers honest enough to announce their size).
/// MCP bodies are JSON or SSE, so lossy UTF-8 matches `.text()` behavior.
pub(super) async fn read_body_capped(
response: reqwest::Response,
max_bytes: usize,
) -> Result<String> {
use futures_util::StreamExt;
let mut stream = response.bytes_stream();
let mut buf: Vec<u8> = Vec::new();
while let Some(chunk) = stream.next().await {
let chunk = chunk.context("failed to read MCP response body")?;
if buf.len().saturating_add(chunk.len()) > max_bytes {
anyhow::bail!("MCP response body exceeds {max_bytes} bytes — aborting");
}
buf.extend_from_slice(&chunk);
}
Ok(String::from_utf8_lossy(&buf).into_owned())
}
fn is_streamable_http_incompatible_status(status: StatusCode) -> bool {
matches!(
status,
StatusCode::NOT_FOUND
| StatusCode::METHOD_NOT_ALLOWED
| StatusCode::NOT_ACCEPTABLE
| StatusCode::UNSUPPORTED_MEDIA_TYPE
| StatusCode::NOT_IMPLEMENTED
)
}
fn is_streamable_http_stale_session_status(status: StatusCode, body_excerpt: &str) -> bool {View on GitHub (pinned to 8880682c63)
Solutions
- Reduce the response size server-side: pagination, filters, returning references
- Check whether a gateway error page (huge HTML) replaced the real MCP response and fix the routing
- Ensure origin servers emit bounded bodies even when a proxy re-chunks them
Defensive patterns
Strategy: fallback
Try / catch
Identical to the declared-length variant: fall back to a narrower, paginated request; retrying the same call reproduces the same stream:
```rust
match fetch_resource(&id).await {
Err(e) if e.to_string().contains("MCP response body exceeds") => {
fetch_resource_range(&id, offset, limit).await
}
other => other?,
}
``` Prevention
- Cap tool output size server-side below the client's 16 MiB response ceiling
- Watch for gateway error pages replacing real MCP responses — they can be the oversized body
When it happens
Trigger: A chunked or length-less MCP HTTP response whose accumulated body exceeds 16 MiB during the capped read.
Common situations: Servers streaming large tool outputs without a length; proxies re-chunking and stripping Content-Length; hostile servers lying about or omitting size.
Related errors
- MCP response Content-Length {len} exceeds {} bytes — abortin
- payload too large
- {} exceeded the {}-page catalogue limit
- {} exceeded the {}-item catalogue limit
- {} exceeded the {}-byte aggregate catalogue limit
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/8b644e9e5d965ada.
Report an issue: GitHub.