{"record":{"id":"23a8af01b8bae590","repo":"can1357/oh-my-pi","slug":"unsupported-rpc-protocol-version-version","errorCode":null,"errorMessage":"Unsupported RPC protocol version: ${version}","messagePattern":"Unsupported RPC protocol version: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/modes/rpc/rpc-frame.ts","lineNumber":277,"sourceCode":"\t\tif (serializedFrameBytes(json) <= MAX_RPC_FRAME_BYTES) return `${json}\\n`;\n\t}\n\n\treturn `${JSON.stringify(overflowFrame(compacted))}\\n`;\n}\n\n/** Serialize a complete JSONL frame while enforcing the transport byte ceiling. */\nexport function encodeRpcFrame(frame: object, streamedMessageCount = 0, streamedMessages?: readonly unknown[]): string {\n\treturn encodeRpcFrameFromJson(frame, JSON.stringify(frame), streamedMessageCount, streamedMessages);\n}\n\n/** Stateful encoder that tracks which messages a client has already received. */\nexport class RpcFrameEncoder {\n\t#streamedMessages: unknown[] = [];\n\t#protocolVersion: RpcProtocolVersion = 1;\n\t#chunkCounter = 0;\n\n\tsetProtocolVersion(version: number): void {\n\t\tif (version !== 1 && version !== 2) throw new Error(`Unsupported RPC protocol version: ${version}`);\n\t\tthis.#protocolVersion = version;\n\t}\n\n\t/**\n\t * Encode one logical frame into physical JSONL lines. Encoder bookkeeping runs\n\t * eagerly; only chunk emission is lazy, so a chunked result can be streamed to\n\t * stdout with backpressure without holding the whole transport in memory. The\n\t * returned iterable MUST be fully consumed exactly once.\n\t */\n\tencodeFrames(frame: object): Iterable<string> {\n\t\tif (isRecord(frame) && frame.type === \"agent_start\") this.#streamedMessages = [];\n\t\tconst json = JSON.stringify(frame);\n\t\tlet frames: Iterable<string>;\n\t\tlet singleFrame: string | undefined;\n\t\tif (this.#protocolVersion === 2 && serializedFrameBytes(json) > MAX_RPC_FRAME_BYTES) {\n\t\t\tconst compacted = compactTerminalFrame(frame, this.#streamedMessages.length, this.#streamedMessages);\n\t\t\t// Reuse the original serialization when compaction was a no-op.\n\t\t\tconst compactedJson = compacted === frame ? json : JSON.stringify(compacted);","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/rpc/rpc-frame.ts#L259-L295","documentation":"RpcFrameEncoder.setProtocolVersion() only accepts protocol versions 1 or 2. Passing any other version number throws immediately, since the encoder cannot emit frames for an unknown wire protocol.","triggerScenarios":"Calling encoder.setProtocolVersion(3) (or 0, -1, 2.5, NaN) during RPC client/server handshake when the peers advertise an unsupported protocolVersion.","commonSituations":"Client and server are different builds with mismatched protocol versions; a new protocol version was introduced in the server but the embedded/older encoder doesn't know it; handshake code forwards a raw string ('2') instead of a number.","solutions":["Only call setProtocolVersion with 1 or 2; clamp or map other values before the call.","Ensure the handshake passes a number, not a string: setProtocolVersion(Number(handshake.version)).","Upgrade both processes to matching versions of the RPC implementation so the advertised version is supported.","Default to version 1 when the negotiated version is unknown rather than forwarding it blindly."],"exampleFix":"// before\nencoder.setProtocolVersion(handshake.protocolVersion); // e.g. 3 from newer peer\n// after\nconst v = handshake.protocolVersion;\nencoder.setProtocolVersion(v === 2 ? 2 : 1);","handlingStrategy":"validation","validationCode":"const SUPPORTED_VERSIONS = [1, 2] as const;\nfunction assertSupportedVersion(v: number): asserts v is 1 | 2 {\n  if (v !== 1 && v !== 2) throw new Error(`client cannot speak protocol ${v}`);\n}\nassertSupportedVersion(handshake.protocolVersion);","typeGuard":"function isSupportedProtocolVersion(v: unknown): v is 1 | 2 {\n  return v === 1 || v === 2;\n}","tryCatchPattern":"try {\n  encoder.setProtocolVersion(negotiated);\n} catch (err) {\n  if (String(err.message).startsWith(\"Unsupported RPC protocol version\")) {\n    encoder.setProtocolVersion(1); // graceful fallback\n  } else throw err;\n}","preventionTips":["Negotiate protocol version numerically, not as a string","Keep both peers on the same build/version of the RPC layer","Clamp or fall back instead of forwarding unknown versions","Log the negotiated version at connection setup for debugging"],"tags":["rpc","protocol","versioning","compatibility"],"backgroundTag":"unsupported-protocol-version","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}