{"record":{"id":"55665f9c7d1d344d","repo":"can1357/oh-my-pi","slug":"rpc-chunk-sequence-mismatch","errorCode":null,"errorMessage":"rpc chunk sequence mismatch","messagePattern":"rpc chunk sequence mismatch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/modes/rpc/rpc-frame.ts","lineNumber":175,"sourceCode":"\t\t\tbyteLength < MAX_RPC_FRAME_BYTES ||\n\t\t\tbyteLength > MAX_RPC_REASSEMBLED_BYTES\n\t\t)\n\t\t\tthrow new Error(\"invalid rpc chunk metadata\");\n\t\tconst bytes = decodeBase64(value.data);\n\t\tif (bytes.byteLength > RPC_CHUNK_PAYLOAD_BYTES) throw new Error(\"rpc chunk payload exceeds the transport limit\");\n\n\t\tif (!this.#pending) {\n\t\t\tif (index !== 0) throw new Error(\"rpc chunk sequence must start at index 0\");\n\t\t\tthis.#pending = { chunkId, count, byteLength, nextIndex: 0, chunks: [], receivedBytes: 0 };\n\t\t}\n\t\tconst pending = this.#pending;\n\t\tif (\n\t\t\tpending.chunkId !== chunkId ||\n\t\t\tpending.count !== count ||\n\t\t\tpending.byteLength !== byteLength ||\n\t\t\tpending.nextIndex !== index\n\t\t)\n\t\t\tthrow new Error(\"rpc chunk sequence mismatch\");\n\t\tpending.chunks.push(bytes);\n\t\tpending.receivedBytes += bytes.byteLength;\n\t\tpending.nextIndex++;\n\t\tif (pending.receivedBytes > pending.byteLength) throw new Error(\"rpc chunk sequence exceeds declared length\");\n\t\tif (pending.nextIndex < pending.count) return undefined;\n\t\tif (pending.receivedBytes !== pending.byteLength) throw new Error(\"rpc chunk sequence length mismatch\");\n\n\t\tthis.#pending = undefined;\n\t\tconst decoded = new TextDecoder(\"utf-8\", { fatal: true }).decode(Buffer.concat(pending.chunks));\n\t\tconst frame: unknown = JSON.parse(decoded);\n\t\tif (!isRecord(frame)) throw new Error(\"rpc frame must be an object\");\n\t\treturn frame;\n\t}\n}\n\nfunction compactTerminalFrame(\n\tframe: object,\n\tstreamedMessageCount: number,","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/rpc/rpc-frame.ts#L157-L193","documentation":"Once reassembly is in progress, every subsequent rpc_chunk must match the pending sequence exactly: same chunkId, count, and byteLength, and its index must equal the expected nextIndex. This error is thrown when any of those fields differs — the chunk belongs to a different sequence, the header was mutated, or chunks arrived out of order or duplicated.","triggerScenarios":"Interleaving chunks from two concurrent chunked sends with different chunkIds; resending a chunk after a retry (duplicate index); sender recomputing count/byteLength mid-sequence; out-of-order delivery of chunk lines.","commonSituations":"Retrying a failed write without dropping decoder state; concurrent agent responses multiplexed on one stream; a buggy sender that re-serializes the frame after emitting early chunks.","solutions":["Send chunks strictly in index order, exactly once per sequence, on a single stream.","Keep chunkId/count/byteLength immutable for the whole sequence — compute them once before emitting chunk 0.","On a failed/retried send, restart the sequence with a NEW chunkId from index 0 and reset the decoder.","Serialize chunk emission with a queue if multiple producers share the stream."],"exampleFix":"// before: retry reuses old chunkId mid-stream\nwrite(frameChunks); // first attempt partially sent\nwrite(frameChunks); // duplicate chunkId, wrong nextIndex\n// after: new sequence id and decoder reset\nconst chunkId = `rpc-${crypto.randomUUID()}`;\nwrite(frameChunks.map(c => ({ ...c, chunkId })));","handlingStrategy":"try-catch","validationCode":"// client-side sequencing check before writing the next chunk\nif (pendingState && (chunk.chunkId !== pendingState.chunkId || chunk.index !== pendingState.nextIndex)) {\n  throw new Error(\"sender bug: chunk does not continue the open sequence\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  const frame = decoder.push(parsedLine);\n} catch (err) {\n  if (err instanceof Error && err.message === \"rpc chunk sequence mismatch\") {\n    decoder = new RpcFrameDecoder(); // discard stale sequence; re-request the logical frame\n  } else throw err;\n}","preventionTips":["One chunked sequence at a time per stream; queue concurrent senders.","Never retry a chunk write without a fresh chunkId and index 0 restart.","Compute chunkId/count/byteLength once per frame and freeze them.","Track nextIndex in a shared write path so out-of-order writes fail fast on the sender."],"tags":["rpc","protocol","stream-ordering","deduplication"],"backgroundTag":"rpc-chunk-sequence-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}