can1357/oh-my-pi · error
invalid rpc chunk metadata
Error message
invalid rpc chunk metadata
What it means
Before decoding payload data, push validates the rpc_chunk metadata: chunkId must be a 1–128 char string; index/count/byteLength must be safe integers with count in [2, ceil(64MiB/256KiB)], index < count, and byteLength in [1MiB, 64MiB]. This error is thrown when any of these constraints fails — the chunk header is structurally invalid.
Source
Thrown at packages/coding-agent/src/modes/rpc/rpc-frame.ts:160
if (!isRecord(value)) throw new Error("rpc frame must be an object");
return value;
}
const { chunkId, index, count, byteLength } = value;
if (
typeof chunkId !== "string" ||
chunkId.length === 0 ||
chunkId.length > 128 ||
!Number.isSafeInteger(index) ||
!Number.isSafeInteger(count) ||
!Number.isSafeInteger(byteLength) ||
index < 0 ||
count < 2 ||
count > Math.ceil(MAX_RPC_REASSEMBLED_BYTES / RPC_CHUNK_PAYLOAD_BYTES) ||
index >= count ||
byteLength < MAX_RPC_FRAME_BYTES ||
byteLength > MAX_RPC_REASSEMBLED_BYTES
)
throw new Error("invalid rpc chunk metadata");
const bytes = decodeBase64(value.data);
if (bytes.byteLength > RPC_CHUNK_PAYLOAD_BYTES) throw new Error("rpc chunk payload exceeds the transport limit");
if (!this.#pending) {
if (index !== 0) throw new Error("rpc chunk sequence must start at index 0");
this.#pending = { chunkId, count, byteLength, nextIndex: 0, chunks: [], receivedBytes: 0 };
}
const pending = this.#pending;
if (
pending.chunkId !== chunkId ||
pending.count !== count ||
pending.byteLength !== byteLength ||
pending.nextIndex !== index
)
throw new Error("rpc chunk sequence mismatch");
pending.chunks.push(bytes);
pending.receivedBytes += bytes.byteLength;
pending.nextIndex++;View on GitHub (pinned to 9690622007)
Solutions
- Only chunk frames whose serialized size exceeds MAX_RPC_FRAME_BYTES; send everything else as one line.
- Set byteLength to the UTF-8 byte length of the ENTIRE logical JSON frame, not the chunk payload.
- Set count = ceil(byteLength / 256KiB) (always ≥ 2 for chunked frames) and index from 0 to count-1.
- Keep chunkId a non-empty string of at most 128 characters, unique per logical frame.
Example fix
// before: byteLength is the chunk size
{ type: "rpc_chunk", chunkId, index, count, byteLength: chunk.length, data }
// after: byteLength is the whole frame's UTF-8 length
const byteLength = Buffer.byteLength(json, "utf8");
const count = Math.ceil(byteLength / (256 * 1024));
{ type: "rpc_chunk", chunkId, index, count, byteLength, data } Defensive patterns
Strategy: validation
Validate before calling
import { MAX_RPC_FRAME_BYTES, MAX_RPC_REASSEMBLED_BYTES } from "./rpc-frame";
const CHUNK = 256 * 1024;
function validateChunkHeader(chunk: { chunkId: string; index: number; count: number; byteLength: number }): void {
if (!(chunk.chunkId.length > 0 && chunk.chunkId.length <= 128)) throw new Error("bad chunkId");
if (chunk.count < 2 || chunk.count > Math.ceil(MAX_RPC_REASSEMBLED_BYTES / CHUNK)) throw new Error("bad count");
if (chunk.index < 0 || chunk.index >= chunk.count) throw new Error("bad index");
if (chunk.byteLength < MAX_RPC_FRAME_BYTES || chunk.byteLength > MAX_RPC_REASSEMBLED_BYTES) throw new Error("bad byteLength");
} Type guard
function hasValidChunkMetadata(v: unknown): v is { type: "rpc_chunk"; chunkId: string; index: number; count: number; byteLength: number; data: string } {
return typeof v === "object" && v !== null &&
typeof (v as any).chunkId === "string" && (v as any).chunkId.length > 0 &&
Number.isSafeInteger((v as any).index) && Number.isSafeInteger((v as any).count) &&
Number.isSafeInteger((v as any).byteLength);
} Try / catch
try {
decoder.push(parsedLine);
} catch (err) {
if (err instanceof Error && err.message === "invalid rpc chunk metadata") {
logger.error("sender produced invalid rpc_chunk header; aborting reassembly", { parsedLine });
} else throw err;
} Prevention
- Only chunk frames over 1 MiB; send smaller frames as single lines.
- byteLength = Buffer.byteLength(entireFrameJson, 'utf8'), not per-chunk size.
- count = ceil(byteLength / 262144); index strictly 0..count-1.
- Reuse the library's own encoder (RpcFrameEncoder) rather than hand-crafting chunks.
When it happens
Trigger: Hand-crafting rpc_chunk frames with wrong fields (e.g. count=1 for a small frame, byteLength of the chunk instead of the whole logical frame, missing byteLength); a sender whose logical frame is under 1 MiB (byteLength < MAX_RPC_FRAME_BYTES is rejected — only oversized frames are ever chunked); index ≥ count; non-integer or negative values.
Common situations: Implementing a third-party sender against protocol v2 and getting the header semantics wrong; sending a chunked frame for a small payload that should have been a single line; off-by-one in count; byteLength set per-chunk instead of per-logical-frame.
Related errors
- invalid rpc chunk data
- rpc frame must be an object
- rpc chunk payload exceeds the transport limit
- rpc chunk sequence exceeds declared length
- RPC frame must be a JSON object
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/c43e3730f0cc843c.
Report an issue: GitHub.