can1357/oh-my-pi · error

invalid rpc chunk data

Error message

invalid rpc chunk data

What it means

decodeBase64 validates the `data` field of an rpc_chunk frame before decoding: it must be a non-empty string containing only valid base64 characters in valid padding shape. This error is thrown when the regex check fails, i.e. the chunk payload is missing, empty, or not syntactically valid base64.

Source

Thrown at packages/coding-agent/src/modes/rpc/rpc-frame.ts:129

		};
		const line = `${JSON.stringify(chunk)}\n`;
		if (serializedFrameBytes(line.slice(0, -1)) > MAX_RPC_FRAME_BYTES)
			throw new Error("RPC chunk exceeded the transport limit");
		yield line;
	}
}

function isRpcChunkFrame(value: unknown): value is RpcChunkFrame {
	return isRecord(value) && value.type === "rpc_chunk";
}

function decodeBase64(data: unknown): Buffer {
	if (
		typeof data !== "string" ||
		data.length === 0 ||
		!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(data)
	)
		throw new Error("invalid rpc chunk data");
	const bytes = Buffer.from(data, "base64");
	if (bytes.toString("base64") !== data) throw new Error("invalid rpc chunk data");
	return bytes;
}

/** Reassemble protocol v2 chunk frames after each JSONL line has been parsed. */
export class RpcFrameDecoder {
	#pending?: PendingRpcChunks;

	push(value: unknown): object | undefined {
		if (!isRpcChunkFrame(value)) {
			if (this.#pending) throw new Error("rpc chunk sequence interrupted");
			if (!isRecord(value)) throw new Error("rpc frame must be an object");
			return value;
		}
		const { chunkId, index, count, byteLength } = value;
		if (
			typeof chunkId !== "string" ||

View on GitHub (pinned to 9690622007)

Solutions

  1. Encode chunk payloads with Buffer.from(bytes).toString('base64') (standard alphabet, correct padding) on the sender side.
  2. Verify the JSONL transport preserves the line intact — no newline/whitespace injection inside the data field.
  3. Check that `data` is actually populated on the chunk frame before sending.

Example fix

// before: URL-safe base64 from a web client
const data = btoa(String.fromCharCode(...bytes)).replace(/\+/g, '-').replace(/\//g, '_');
// after: standard base64
const data = Buffer.from(bytes).toString('base64');
Defensive patterns

Strategy: validation

Validate before calling

function isValidBase64Shape(data: unknown): data is string {
  return typeof data === "string" &&
    data.length > 0 &&
    /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(data);
}
// pre-send check
if (!isValidBase64Shape(chunk.data)) throw new Error("refusing to send non-canonical chunk data");

Type guard

function isCanonicalBase64(data: unknown): data is string {
  if (typeof data !== "string" || data.length === 0) return false;
  if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(data)) return false;
  return Buffer.from(data, "base64").toString("base64") === data;
}

Try / catch

try {
  decoder.push(parsedLine);
} catch (err) {
  if (err instanceof Error && err.message === "invalid rpc chunk data") {
    logger.warn("dropping rpc_chunk with malformed base64 payload", { chunkId: parsedLine?.chunkId });
  } else throw err;
}

Prevention

When it happens

Trigger: Pushing a parsed rpc_chunk object to RpcFrameDecoder.push where `data` is undefined/null/empty string, contains whitespace/newlines (e.g. line-splitting mangled it), uses URL-safe base64 (-/_) instead of standard, or has wrong padding.

Common situations: A hand-rolled client URL-safe-encodes payloads; a proxy re-wraps JSONL lines and inserts CRLF inside the data field; a test fixture passes a placeholder string; the sender wrote an empty final chunk.

Related errors


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