can1357/oh-my-pi · error · RpcError

RPC chunk sequence was interrupted

Error message

RPC chunk sequence was interrupted

What it means

_RpcFrameDecoder.push() reassembles multi-frame `rpc_chunk` sequences. Once a chunk sequence is pending, the very next frame must be another `rpc_chunk` of the same chunkId; if any non-chunk frame (or non-dict value) arrives first, it raises RpcError("RPC chunk sequence was interrupted"). This preserves protocol integrity — a large RPC response whose chunks were interleaved with or truncated by other frames cannot be reassembled safely.

Source

Thrown at python/omp-rpc/src/omp_rpc/client.py:144

@dataclass(slots=True)
class _PendingRpcChunks:
    chunk_id: str
    count: int
    byte_length: int
    next_index: int = 0
    chunks: list[bytes] = field(default_factory=list)
    received_bytes: int = 0


class _RpcFrameDecoder:
    def __init__(self) -> None:
        self._pending: _PendingRpcChunks | None = None

    def push(self, value: object) -> JsonObject | None:
        if not isinstance(value, dict) or value.get("type") != "rpc_chunk":
            if self._pending is not None:
                raise RpcError("RPC chunk sequence was interrupted")
            if not isinstance(value, dict):
                raise RpcError("RPC frame must be a JSON object")
            return cast(JsonObject, value)

        chunk_id = value.get("chunkId")
        index = value.get("index")
        count = value.get("count")
        byte_length = value.get("byteLength")
        data = value.get("data")
        max_chunk_count = (
            _MAX_RPC_REASSEMBLED_BYTES + _RPC_CHUNK_PAYLOAD_BYTES - 1
        ) // _RPC_CHUNK_PAYLOAD_BYTES
        if (
            not isinstance(chunk_id, str)
            or not chunk_id
            or len(chunk_id) > 128
            or not isinstance(index, int)
            or isinstance(index, bool)

View on GitHub (pinned to 9690622007)

Solutions

  1. Ensure the peer emits all chunks of a chunkId contiguously (same chunkId, indices 0..count-1) with no intervening frames.
  2. Update the client/server to a matching protocol version that both support chunked responses.
  3. Catch RpcError and resynchronize: reset/reconnect the RPC stream, since decoder state (_pending) must be discarded.
  4. Check for concurrent writers on the transport — serialize sends with a lock.

Example fix

# before: interleaving a status event mid-transfer
send({"type": "event", ...})   # interrupts pending chunks
# after: buffer events until the chunk sequence completes
send_all_chunks_first(chunk_id, chunks)
send({"type": "event", ...})
Defensive patterns

Strategy: try-catch

Validate before calling

# Validate frames before feeding the decoder: once a rpc_chunk sequence starts,
# no other frame type may be sent until all `count` chunks arrive.
def validate_chunk_stream(frames):
    pending = False
    for f in frames:
        if pending and not (isinstance(f, dict) and f.get("type") == "rpc_chunk"):
            raise ValueError("non-chunk frame inside a chunked RPC sequence")
        pending = isinstance(f, dict) and f.get("type") == "rpc_chunk"

Type guard

def is_rpc_chunk(value: object) -> bool:
    return isinstance(value, dict) and value.get("type") == "rpc_chunk"

Try / catch

decoder = _RpcFrameDecoder()
try:
    frame = decoder.push(raw_value)
except RpcError as e:
    if 'chunk sequence was interrupted' in str(e):
        decoder = _RpcFrameDecoder()  # reset decoder state, then reconnect/resync
        raise ConnectionError('RPC chunked response corrupted; stream resynchronized') from e
    raise

Prevention

When it happens

Trigger: A server streaming a chunked RPC response sends a regular frame (event, result, error) between chunks; the chunked message is truncated mid-stream and another message follows; a malformed client injects frames into the stream; multiple writers share the transport without chunk-aware serialization.

Common situations: Custom/older RPC peers that don't implement chunked framing; proxies or loggers rewriting the frame stream; a >256KB RPC payload (forcing chunking) combined with concurrent events on the same connection.

Related errors


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