{"record":{"id":"cb96c2c1b1671bc2","repo":"can1357/oh-my-pi","slug":"invalid-json-rpc-message","errorCode":null,"errorMessage":"Invalid JSON-RPC message","messagePattern":"Invalid JSON-RPC message","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/utils/src/acp/stream.ts","lineNumber":79,"sourceCode":"\t\t\t\tcontroller.error(error);\n\t\t\t} finally {\n\t\t\t\treader.releaseLock();\n\t\t\t}\n\t\t},\n\t});\n\treturn { writable, readable };\n}\n\nfunction parseMessage(line: string): AnyMessage {\n\tconst value: unknown = JSON.parse(line);\n\tif (\n\t\ttypeof value !== \"object\" ||\n\t\tvalue === null ||\n\t\tArray.isArray(value) ||\n\t\t!(\"jsonrpc\" in value) ||\n\t\tvalue.jsonrpc !== \"2.0\"\n\t) {\n\t\tthrow new Error(\"Invalid JSON-RPC message\");\n\t}\n\treturn value as AnyMessage;\n}\n","sourceCodeStart":61,"sourceCodeEnd":83,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/acp/stream.ts#L61-L83","documentation":"parseMessage validates each newline-delimited line read by ndJsonStream as a JSON-RPC 2.0 message: it must parse to a non-null, non-array object containing 'jsonrpc' exactly equal to \"2.0\". Any line failing this throws 'Invalid JSON-RPC message', which errors the readable stream (via controller.error in start).","triggerScenarios":"The connected peer writes a JSON line without a jsonrpc field, uses jsonrpc \"1.0\" or a missing version, emits a bare JSON array, a JSON literal (number/string/bool/null) on its own line, or plain-text/log output (e.g. startup banners, debug prints) interleaved with the newline-delimited protocol stream.","commonSituations":"Spawning an ACP agent that prints logs to stdout instead of stderr; protocol version mismatch with an older/newer peer; a JSON-RPC 1.0 server; child process emitting progress or banner text on the protocol channel.","solutions":["Fix the peer so every protocol line is a JSON-RPC 2.0 object ({\"jsonrpc\":\"2.0\",...}) and route logs to stderr.","Check for protocol/version mismatch and upgrade or configure the agent/client to JSON-RPC 2.0.","Attach an error handler on the stream's readable side to capture and diagnose the offending line.","Strip or filter non-protocol stdout output before feeding the stream, or use a transport that frames messages instead of assuming clean NDJSON."],"exampleFix":"// before\nconsole.log(\"agent ready\"); // lands on stdout, corrupts the stream\n// after\nconsole.error(\"agent ready\"); // stderr keeps the protocol channel clean","handlingStrategy":"validation","validationCode":"function looksLikeJsonRpc(line: string): boolean {\n  try {\n    const v = JSON.parse(line);\n    return typeof v === \"object\" && v !== null && !Array.isArray(v) && (v as any).jsonrpc === \"2.0\";\n  } catch { return false; }\n}","typeGuard":"function isJsonRpcMessage(v: unknown): v is { jsonrpc: \"2.0\" } & Record<string, unknown> {\n  return typeof v === \"object\" && v !== null && !Array.isArray(v)\n    && \"jsonrpc\" in v && (v as Record<string, unknown>).jsonrpc === \"2.0\";\n}","tryCatchPattern":"try {\n  for await (const msg of stream.readable) handle(msg);\n} catch (err) {\n  if (err instanceof Error && err.message === \"Invalid JSON-RPC message\") {\n    console.error(\"peer sent a non-JSON-RPC-2.0 line; check for stdout logs or version mismatch\");\n  } else throw err;\n}","preventionTips":["Ensure the peer writes ONLY JSON-RPC 2.0 NDJSON on the protocol channel; route all logs to stderr.","Verify both sides negotiate the same protocol version (jsonrpc \"2.0\").","Handle stream.readable errors so one bad line surfaces as a diagnosable failure, not a hang.","Test agents with a strict NDJSON linter before wiring them into the transport."],"tags":["json-rpc","acp","protocol","stream"],"backgroundTag":"invalid-json-rpc-message","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}