can1357/oh-my-pi · error
rpc chunk sequence interrupted
Error message
rpc chunk sequence interrupted
What it means
RpcFrameDecoder is a stateful reassembler: once it has received rpc_chunk frames for an in-progress logical frame (#pending set), the very next JSONL line must be the following chunk of that sequence. This error is thrown when a non-chunk frame arrives while a chunked sequence is still open, meaning the sequence was interrupted mid-stream.
Source
Thrown at packages/coding-agent/src/modes/rpc/rpc-frame.ts:141
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" ||
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
)View on GitHub (pinned to 9690622007)
Solutions
- Emit all chunks of a logical frame back-to-back on the same stream with no interleaved frames.
- Serialize chunked-frame emission behind a lock/queue if multiple producers share the stream.
- On abort, discard the decoder state (create a new RpcFrameDecoder) or complete the sequence with a valid reassembly.
Example fix
// before: interleaving events between chunks for (const c of chunks) write(c); write(otherFrame); // after: emit chunks contiguously for (const c of chunks) write(c); // then other frames
Defensive patterns
Strategy: try-catch
Try / catch
try {
decoder.push(parsedLine);
} catch (err) {
if (err instanceof Error && err.message === "rpc chunk sequence interrupted") {
decoder = new RpcFrameDecoder(); // drop partial state; re-request the frame
} else throw err;
} Prevention
- Emit all chunks of a logical frame contiguously with no interleaved frames.
- Funnel all stream writers through a single queue.
- Recreate the decoder after any abort instead of continuing with stale state.
When it happens
Trigger: Interleaving a normal RPC frame (response, event, etc.) between rpc_chunk lines; a client sending two chunked frames concurrently; the sender aborting mid-sequence and sending a different frame without resetting the decoder.
Common situations: Multiplexing two agents' output onto one RPC stream; logging/heartbeat lines injected between chunks; replaying a partial capture that truncates a sequence then continues with other frames.
Related errors
- rpc chunk sequence must start at index 0
- rpc chunk sequence mismatch
- Replacement text is not valid UTF-8: {err}
- invalid glob `{pattern}`: {error}
- RPC chunk received before protocol negotiation
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/de664b542954ae79.
Report an issue: GitHub.