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

  1. Fix the MCP server to emit one compact JSON-RPC object per line and keep logs on stderr
  2. Ensure the command in the MCP config actually launches the MCP server, not a wrapper that prints extra output
  3. If legitimate payloads are large, raise the line-size limit in the client configuration
  4. 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

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


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)