{"record":{"id":"acc6b9862ba37402","repo":"can1357/oh-my-pi","slug":"rpc-chunk-sequence-exceeds-declared-length","errorCode":null,"errorMessage":"rpc chunk sequence exceeds declared length","messagePattern":"rpc chunk sequence exceeds declared length","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/modes/rpc/rpc-frame.ts","lineNumber":179,"sourceCode":"\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,\n\tstreamedMessages?: readonly unknown[],\n): object {\n\tif (!isRecord(frame) || frame.type !== \"agent_end\" || !Array.isArray(frame.messages)) return frame;\n\tlet streamed = Number.isSafeInteger(streamedMessageCount)","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/rpc/rpc-frame.ts#L161-L197","documentation":"As chunks accumulate, push tracks receivedBytes against the sequence's declared byteLength. This error is thrown as soon as accumulated chunk payload bytes exceed the declared total, proving the frame's byteLength header lied (or an extra oversized chunk slipped in) — caught before any full-payload allocation.","triggerScenarios":"Sender declaring byteLength smaller than the actual UTF-8 size of the logical frame (e.g. using .length characters instead of Buffer.byteLength for multi-byte UTF-8); an extra chunk appended beyond count; oversized chunks that individually pass the 256KiB check but sum past the header.","commonSituations":"Hand-rolled senders measuring the JSON string with string length instead of byte length (CJK/emoji payloads inflate UTF-8 size); byteLength computed on the base64 form instead of raw bytes; duplicate chunk sends inflating receivedBytes.","solutions":["Compute byteLength with Buffer.byteLength(json, 'utf8'), never json.length.","Ensure sum of per-chunk decoded byte counts equals byteLength exactly — no extra or oversized chunks.","Compute byteLength from the same exact bytes that are sliced into chunks (serialize once, reuse the buffer)."],"exampleFix":"// before: character count, not bytes\nconst byteLength = json.length;\n// after: UTF-8 byte length\nconst byteLength = Buffer.byteLength(json, \"utf8\");","handlingStrategy":"validation","validationCode":"// sender-side pre-flight: byteLength must be exact UTF-8 byte count\nconst bytes = Buffer.from(json, \"utf8\");\nconst byteLength = bytes.byteLength; // NOT json.length\nlet total = 0;\nfor (let i = 0; i < byteLength; i += 256 * 1024) {\n  total += Math.min(256 * 1024, byteLength - i);\n}\nif (total !== byteLength) throw new Error(\"chunk split does not sum to byteLength\");","typeGuard":null,"tryCatchPattern":"try {\n  const frame = decoder.push(parsedLine);\n} catch (err) {\n  if (err instanceof Error && err.message === \"rpc chunk sequence exceeds declared length\") {\n    logger.error(\"sender byteLength header understates payload; discarding sequence\");\n    decoder = new RpcFrameDecoder();\n  } else throw err;\n}","preventionTips":["Always use Buffer.byteLength(json, 'utf8') for byteLength, never string .length.","Serialize once and slice the same buffer you measure.","Add a sender-side unit test with multi-byte UTF-8 payloads."],"tags":["rpc","protocol","utf-8","validation"],"backgroundTag":"rpc-chunk-length-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}