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

  1. Only chunk frames whose serialized size exceeds MAX_RPC_FRAME_BYTES; send everything else as one line.
  2. Set byteLength to the UTF-8 byte length of the ENTIRE logical JSON frame, not the chunk payload.
  3. Set count = ceil(byteLength / 256KiB) (always ≥ 2 for chunked frames) and index from 0 to count-1.
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/c43e3730f0cc843c. Report an issue: GitHub.