Hmbown/CodeWhale · error · io::Error
JSON-RPC line exceeded the
Error message
JSON-RPC line exceeded the {max_bytes}-byte limit What it means
read_bounded_line enforces a per-line byte cap (max_bytes) on JSON-RPC frames read from the MCP server child's stdout so a misbehaving or malicious server cannot exhaust memory. When a newline is found and the accumulated line plus the bytes up to it would exceed max_bytes, it returns InvalidData with this message. The whole oversized line is discarded — the connection is treated as unusable.
Solutions
- Fix the MCP server to emit one compact JSON-RPC object per line and keep logs on stderr
- Ensure the command in the MCP config actually launches the MCP server, not a wrapper that prints extra output
- If legitimate payloads are large, raise the line-size limit in the client configuration
- Capture the server's raw stdout once to identify what it is actually emitting
Example fix
// before: server prints diagnostics to stdout
println!("loaded {} tools", n); // pollutes JSON-RPC stream
// after
eprintln!("loaded {} tools", n); // stderr only Defensive patterns
Strategy: validation
Validate before calling
// smoke-test the server's stdout before wiring it up
const MAX_LINE: usize = 1 << 20; // match max_bytes
proc.stdout.lines().take(1).for_each(|l|
assert!(l.unwrap().len() < MAX_LINE, "server line exceeds limit")); Try / catch
match client.call(tool, args) {
Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().contains("byte limit") => {
eprintln!("MCP server emitted an oversized/non-protocol line; check its stdout");
restart_server();
}
r => r,
} Prevention
- Reserve stdout exclusively for line-delimited JSON-RPC; log to stderr
- Smoke-test server startup output before production use
- Keep legitimate payloads well under max_bytes or raise the limit deliberately
- Never wrap the MCP server in scripts that echo to stdout
When it happens
Trigger: An MCP stdio server emits a single JSON-RPC message (up to and including its newline) longer than max_bytes; the error surfaces in spawn_with_timeouts while reading the handshake or any response.
Common situations: A server that pretty-prints or logs huge blobs on stdout instead of speaking line-delimited JSON-RPC; a non-MCP program (build noise, banners) launched as the server; a server stuck in a loop dumping data; max_bytes misconfigured far below real payload sizes (large tool results).
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
- MCP stdio line exceeded
- child returned a malformed MCP CallToolResult
- child stdout was not valid UTF-8
- connection stdin poisoned by an earlier panic
- exceeded the -item catalogue limit
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/cef4f884c2cf30e7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/mcp/src/stdio_client.rs:239
/// `max_bytes`. On an oversized line the reader is intentionally abandoned;
/// continuing after losing JSON-RPC framing would be unsafe.
pub(crate) fn read_bounded_line<R: BufRead>(
reader: &mut R,
max_bytes: usize,
) -> io::Result<Option<String>> {
let mut line = Vec::new();
loop {
let available = reader.fill_buf()?;
if available.is_empty() {
if line.is_empty() {
return Ok(None);
}
break;
}
if let Some(newline) = available.iter().position(|byte| *byte == b'\n') {
if line.len().saturating_add(newline) > max_bytes {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("JSON-RPC line exceeded the {max_bytes}-byte limit"),
));
}
line.extend_from_slice(&available[..newline]);
reader.consume(newline + 1);
break;
}
if line.len().saturating_add(available.len()) > max_bytes {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("JSON-RPC line exceeded the {max_bytes}-byte limit"),
));
}
line.extend_from_slice(available);
let consumed = available.len();
reader.consume(consumed);View on GitHub (pinned to 73e0f67d83)