block/buzz · error · PARSE_ERROR
-32700
-32700
Error message
jsonrpc: parse: {e} What it means
The buzz-agent harness speaks newline-delimited JSON-RPC over stdin. read_loop parses each non-empty line with serde_json; a line that is not valid JSON gets a JSON-RPC error response with code -32700 (parse error) and id null, and the loop continues with the next line — one bad line does not kill the agent.
Source
Thrown at crates/buzz-agent/src/lib.rs:275
}
}
async fn read_loop<R: tokio::io::AsyncBufRead + Unpin>(
mut stdin: R,
app: Arc<App>,
wire_tx: WireSender,
max_line: usize,
) -> std::io::Result<()> {
while let Some(line) = wire::read_bounded_line(&mut stdin, max_line).await? {
if line.trim().is_empty() {
continue;
}
match serde_json::from_str::<Value>(&line) {
Ok(msg) => dispatch(&app, msg, &wire_tx).await,
Err(e) => {
wire::send(
&wire_tx,
wire::err(Value::Null, PARSE_ERROR, &format!("jsonrpc: parse: {e}")),
)
.await;
}
}
}
Ok(())
}
async fn dispatch(app: &Arc<App>, msg: Value, wire_tx: &WireSender) {
match classify(&msg) {
Inbound::Request { id, method, params } => {
handle_request(app, id, method, params, wire_tx).await
}
Inbound::Notification { method, params } => handle_notification(app, &method, params).await,
// Client's answer to a `session/request_permission` we issued. The
// broker matches it to a live correlation id (waking that waiter) or
// ignores an unknown/late id.
Inbound::Response { id, result } => app.permissions.deliver(&id, result),View on GitHub (pinned to dad5a33865)
Solutions
- Write exactly one compact, single-line JSON-RPC message per line and flush after each write
- Validate every outgoing message with a JSON serializer (never concatenate hand-built strings) before writing to stdin
- If the client is LSP-framed, strip Content-Length framing and emit raw NDJSON
- Check the spawning script for stray prints/echoes that pollute the stdin pipe
Example fix
// before (client, Node.js)
process.stdin.write(JSON.stringify(msg, null, 2)); // multi-line → -32700
// after
const line = JSON.stringify(msg); // compact, single line
if (line.includes("\n")) throw new Error("jsonrpc message spans multiple lines");
agent.stdin.write(line + "\n"); Defensive patterns
Strategy: validation
Validate before calling
function writeJsonRpc(stdin: NodeJS.WritableStream, msg: unknown): void {
if (typeof msg !== "object" || msg === null) throw new TypeError("jsonrpc message must be an object");
const line = JSON.stringify(msg); // compact — never pretty print
if (line.includes("\n")) throw new Error("jsonrpc message must serialize to a single line");
stdin.write(line + "\n");
} Try / catch
// client side: a -32700 response means YOUR writer is broken, not the agent
if (resp.error?.code === -32700) {
throw new Error(`framing bug: our last stdin line was not valid JSON: ${lastLineSent}`);
} Prevention
- Always serialize with a real JSON library; never hand-concatenate message strings
- One compact message per line, flushed per write — no Content-Length framing, no pretty printing
- Keep the agent's stdin pipe dedicated; route wrapper-script diagnostics to stderr
When it happens
Trigger: Sending LSP-style framed messages (a 'Content-Length: 123' header line) instead of one JSON object per line; pretty-printed JSON split across multiple lines; a UTF-8 BOM prefix; stray non-JSON text (log lines, shell echo from a wrapper script) written into the agent's stdin.
Common situations: Clients ported from LSP assuming header framing; spawning scripts whose own stdout leaks into the child's stdin pipe; hand-rolled writers that forget to strip newlines inside string values; multiline JSON pasted into a debug console.
Related errors
AI-assisted analysis of block/buzz@dad5a33865 (2026-08-20).
Data as JSON: /api/errors/57fd1254926fe8c1.
Report an issue: GitHub.