Hmbown/CodeWhale · error
failed to read bounded stdio JSON-RPC frame
Error message
failed to read bounded stdio JSON-RPC frame: {err} What it means
The stdio JSON-RPC transport reads length/size-bounded frames (lines) from the child process. When reading a frame fails (IO error, oversized/bounded-frame violation), the transport emits a JSON-RPC parse error response and then bails with this message naming the underlying error. It signals the stdio channel with the MCP child is broken or misbehaving.
Solutions
- Check the child process is alive and healthy (exit status, stderr logs); restart the MCP server process and retry.
- Reduce frame size (limit tool output / request size) or raise the configured read bound if it is genuinely too small.
- Inspect the embedded {err} for the root cause: pipe closed vs size limit vs IO error, and fix accordingly (fix child crash, adjust limit, check permissions).
Defensive patterns
Strategy: retry
Validate before calling
fn child_alive(child: &std::process::Child) -> bool {
matches!(child.try_wait(), Ok(None))
}
// verify child liveness (and that a frame size limit is configured generously) before issuing requests Try / catch
match transport.call(request) {
Err(e) if e.to_string().contains("failed to read bounded stdio JSON-RPC frame") => {
tracing::error!("stdio transport failed: {e}; restarting server");
restart_child()?; // then retry once
}
other => other,
} Prevention
- Monitor child stderr and exit status; restart crashed servers proactively.
- Cap tool output size so frames stay under the read bound.
- Detect broken pipe / child exit early and fail the pending request fast instead of waiting.
When it happens
Trigger: A read of a bounded stdio JSON-RPC frame from the child process returns an error (err), e.g. the frame exceeds the size bound, the pipe closed mid-frame, or an OS-level read failure.
Common situations: Child process crashed or exited while a request was in flight; child emitted a frame larger than the configured bound (huge tool output); broken pipe after the child was killed by OOM or signal; non-JSON-RPC output corrupting the framing.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- failed to read bounded stdio JSON-RPC frame
- child returned a malformed MCP CallToolResult
- child stdout was not valid UTF-8
- connection stdin poisoned by an earlier panic
- JSON-RPC line exceeded the
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/c72141e0177367e3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/mcp/src/lib.rs:906
let mut stdout = io::stdout();
let mut stderr = io::stderr();
let mut state = build_stdio_state(initial_definitions);
let mut input = stdin.lock();
loop {
let line =
match stdio_client::read_bounded_line(&mut input, stdio_client::MAX_JSONRPC_LINE_BYTES)
{
Ok(Some(line)) => line,
Ok(None) => break,
Err(err) => {
let response = jsonrpc_error(
None,
JsonRpcError::parse_error(format!("invalid JSON-RPC frame: {err}")),
);
writeln!(stdout, "{response}")?;
stdout.flush()?;
bail!("failed to read bounded stdio JSON-RPC frame: {err}");
}
};
if line.trim().is_empty() {
continue;
}
let value: Value = match serde_json::from_str(&line) {
Ok(value) => value,
Err(err) => {
let msg = jsonrpc_error(
None,
JsonRpcError::parse_error(format!("invalid json: {err}")),
);
writeln!(stdout, "{msg}")?;
stdout.flush()?;
continue;
}
};View on GitHub (pinned to 73e0f67d83)