Hmbown/CodeWhale · error
MCP response body exceeds
Error message
MCP response body exceeds {max_bytes} bytes — aborting What it means
The streamable-HTTP MCP transport caps response body reads at max_bytes. While streaming the response body, if accumulating another chunk would exceed the cap the read is aborted with this error, protecting the client from unbounded or malicious responses.
Solutions
- Increase the MCP response body size cap in configuration if legitimate responses are large.
- Reduce the size of tool outputs on the server side (paginate, truncate).
- Check the server for a bug that returns oversized non-JSON bodies.
- If the endpoint is untrusted, treat this as the size guard working as intended and investigate the server.
Example fix
// before: cap too small for large tool outputs let max_bytes = 64 * 1024; // after let max_bytes = 4 * 1024 * 1024;
Defensive patterns
Strategy: validation
Validate before calling
if expected_max_response > configured_max_bytes {
raise ConfigError("response cap smaller than expected tool outputs; raise the cap")
} Try / catch
Err(e) if e.to_string().contains("body exceeds") => {
// treat as untrusted/oversized response: do not retry blindly;
// raise cap only if the server is trusted and outputs are legitimately large
return Err(anyhow!("oversized MCP response; cap={}, check server output size", e));
} Prevention
- Size the cap above your largest legitimate tool output but well below memory limits.
- Cap tool outputs server-side (paginate/truncate) so responses stay bounded.
- Treat repeated breaches from one endpoint as a server bug or compromise signal.
When it happens
Trigger: An MCP server returns a response body larger than the configured cap while send() drains the bytes_stream — e.g. a huge tool result, a server bug returning an error page as a giant body, or a compromised/hostile endpoint.
Common situations: Tool returning enormous output (large file dumps, log spew); misconfigured cap set below the server's legitimate response size; server streaming an error document without a proper HTTP error status.
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
- ${cause.message}
- Codewhale responses request should succeed
- ${compactRuntimeError(response.status, body)}
- MCP HTTP destination blocked by network policy
- MCP HTTP destination requires network approval
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/8b644e9e5d965ada.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/streamable_http.rs:228
}
/// 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())
}
/// TUI recovery for a rejected OAuth session. Settings recovery and the
/// Streamable HTTP path share the oauth helpers so `/mcp login <name>` stays
/// the only advertised command; `/mcp auth` is not a command.
fn oauth_refresh_failed_hint() -> &'static str {
super::oauth::tui_reauth_refresh_failed_hint()
}
/// TUI recovery for a rejected OAuth session. `oauth_configured` is the
/// server's configured auth path ([`McpHttpAuth::oauth_configured`]), not the
/// presence of a cached token, so a first-run OAuth server — a 401 with
/// nothing stored yet — is still pointed at `/mcp login <name>` rather than at
/// a bearer token it never had (#6030). Servers where a bearer credential isView on GitHub (pinned to 73e0f67d83)