{"record":{"id":"57fd1254926fe8c1","repo":"block/buzz","slug":"32700","errorCode":"-32700","errorMessage":"jsonrpc: parse: {e}","messagePattern":"jsonrpc: parse: (.+?)","errorType":"error_code","errorClass":"PARSE_ERROR","httpStatus":null,"severity":"error","filePath":"crates/buzz-agent/src/lib.rs","lineNumber":275,"sourceCode":"    }\n}\n\nasync fn read_loop<R: tokio::io::AsyncBufRead + Unpin>(\n    mut stdin: R,\n    app: Arc<App>,\n    wire_tx: WireSender,\n    max_line: usize,\n) -> std::io::Result<()> {\n    while let Some(line) = wire::read_bounded_line(&mut stdin, max_line).await? {\n        if line.trim().is_empty() {\n            continue;\n        }\n        match serde_json::from_str::<Value>(&line) {\n            Ok(msg) => dispatch(&app, msg, &wire_tx).await,\n            Err(e) => {\n                wire::send(\n                    &wire_tx,\n                    wire::err(Value::Null, PARSE_ERROR, &format!(\"jsonrpc: parse: {e}\")),\n                )\n                .await;\n            }\n        }\n    }\n    Ok(())\n}\n\nasync fn dispatch(app: &Arc<App>, msg: Value, wire_tx: &WireSender) {\n    match classify(&msg) {\n        Inbound::Request { id, method, params } => {\n            handle_request(app, id, method, params, wire_tx).await\n        }\n        Inbound::Notification { method, params } => handle_notification(app, &method, params).await,\n        // Client's answer to a `session/request_permission` we issued. The\n        // broker matches it to a live correlation id (waking that waiter) or\n        // ignores an unknown/late id.\n        Inbound::Response { id, result } => app.permissions.deliver(&id, result),","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-agent/src/lib.rs#L257-L293","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (client, Node.js)\nprocess.stdin.write(JSON.stringify(msg, null, 2)); // multi-line → -32700\n\n// after\nconst line = JSON.stringify(msg); // compact, single line\nif (line.includes(\"\\n\")) throw new Error(\"jsonrpc message spans multiple lines\");\nagent.stdin.write(line + \"\\n\");","handlingStrategy":"validation","validationCode":"function writeJsonRpc(stdin: NodeJS.WritableStream, msg: unknown): void {\n  if (typeof msg !== \"object\" || msg === null) throw new TypeError(\"jsonrpc message must be an object\");\n  const line = JSON.stringify(msg); // compact — never pretty print\n  if (line.includes(\"\\n\")) throw new Error(\"jsonrpc message must serialize to a single line\");\n  stdin.write(line + \"\\n\");\n}","typeGuard":null,"tryCatchPattern":"// client side: a -32700 response means YOUR writer is broken, not the agent\nif (resp.error?.code === -32700) {\n  throw new Error(`framing bug: our last stdin line was not valid JSON: ${lastLineSent}`);\n}","preventionTips":["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"],"tags":["jsonrpc","stdin","ndjson","protocol","agent","serde"],"backgroundTag":"jsonrpc-parse-error","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-20T04:38:24.874Z","contentChangedAt":"2026-08-20T04:38:24.874Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}