Hmbown/CodeWhale · error
MCP response Content-Length {len} exceeds {} bytes — abortin
Error message
MCP response Content-Length {len} exceeds {} bytes — aborting What it means
Fast-path guard: the HTTP response declares a Content-Length larger than MAX_MCP_RESPONSE_BYTES (16 MiB, crates/tui/src/mcp/wire.rs), so the body is rejected before any byte is read. This stops a misbehaving or malicious server from OOM-ing the client at transport-read time with an oversized response.
Source
Thrown at crates/tui/src/mcp/streamable_http.rs:124
mask_url_secrets(&self.url),
status,
body_excerpt,
)));
}
let content_type = response
.headers()
.get(CONTENT_TYPE)
.and_then(|value| value.to_str().ok())
.map(str::to_string);
// Reject an over-large declared body before reading anything (fast
// path), then bound the read itself so chunked / length-less
// responses cannot OOM us either — Content-Length alone does not
// protect against a server that streams without declaring a length.
if let Some(len) = response.content_length()
&& len > MAX_MCP_RESPONSE_BYTES as u64
{
return Err(StreamableSendError::Other(anyhow::anyhow!(
"MCP response Content-Length {len} exceeds {} bytes — aborting",
MAX_MCP_RESPONSE_BYTES
)));
}
let body = read_body_capped(response, MAX_MCP_RESPONSE_BYTES)
.await
.map_err(StreamableSendError::Other)?;
self.store_response_body(content_type.as_deref(), &body)
.map_err(StreamableSendError::Other)
}
pub(super) async fn recv(&mut self) -> Result<Vec<u8>> {
self.pending_messages
.pop_front()
.context("MCP Streamable HTTP response queue is empty")
}
fn store_response_body(&mut self, content_type: Option<&str>, body: &str) -> Result<()> {View on GitHub (pinned to 8880682c63)
Solutions
- Shrink the server response: paginate tool results, cap file reads, return references instead of inlining content
- Narrow the tool arguments (offset/limit, path filters, query terms) so each response stays under 16 MiB
- Split the work into multiple smaller tool calls when neither side is under your control
Example fix
// before: server inlines the whole file in one result content = fs.read_text(path).await?; // after: page the content content = fs.read_text_range(path, offset, limit).await?;
Defensive patterns
Strategy: fallback
Try / catch
On this deterministic cap rejection, fall back to a narrower request rather than repeating the call:
```rust
match tool_call(&args).await {
Err(e) if e.to_string().contains("Content-Length") && e.to_string().contains("exceeds") => {
tool_call(&args.with_page(next_page())).await // paginate
}
other => other?,
}
``` Prevention
- Design MCP tools to paginate large outputs by default
- Return references or URIs for large content instead of inlining it in tool results
When it happens
Trigger: A tool result or resource read returns a declared body over 16 MiB — e.g. a filesystem or grep MCP tool inlining an entire large file in one response.
Common situations: Tools without pagination reading big files or logs; servers embedding large base64 blobs; hostile servers probing client limits.
Related errors
- MCP response body exceeds {max_bytes} bytes — aborting
- 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/d09585e2cd2be006.
Report an issue: GitHub.