can1357/oh-my-pi · error · RpcError

RPC frame must be a JSON object

Error message

RPC frame must be a JSON object

What it means

push() is the frame feeder for the RPC client's reassembly pipeline. After handling chunked rpc_chunk frames, any leftover frame must be a JSON object (dict); a non-dict value (list, string, number, bool, null) is rejected because RPC frames are protocol messages keyed by fields like "type", "id", and "method". The library throws this to enforce the wire contract at the client boundary rather than letting a malformed frame flow into dispatch.

Source

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

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)
            or not isinstance(count, int)
            or isinstance(count, bool)

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the sender emits one JSON object per frame (e.g. {"type": "request", ...}), not arrays or scalars.
  2. Inspect what you feed push(): if you parse lines yourself, ensure each line is json.loads'ed to a dict before pushing.
  3. If the peer legitimately streams a top-level array, iterate its elements and push each object individually.
  4. Check peer/library version compatibility with the omp-rpc framing protocol.
  5. Wrap push() in try/except RpcError to detect and drop/inspect malformed frames instead of crashing the pump.

Example fix

// before
for line in proc.stdout:
    frame = json.loads(line)
    client.push(frame)  # crashes if line is '[1,2,3]' or '"hello"'

// after
for line in proc.stdout:
    frame = json.loads(line)
    if isinstance(frame, dict):
        client.push(frame)
    else:
        logger.warning("skipping non-object frame: %r", frame)
Defensive patterns

Strategy: type-guard

Validate before calling

def is_valid_frame(value: object) -> bool:
    return isinstance(value, dict) and "type" in value

Type guard

def is_json_object(value: object) -> TypeGuard[dict]:
    return isinstance(value, dict)

Try / catch

try:
    frame = client.push(value)
except RpcError as exc:
    if str(exc) == "RPC frame must be a JSON object":
        logger.warning("discarding non-object frame: %r", value)
        frame = None
    else:
        raise

Prevention

When it happens

Trigger: Calling client.push(value) with a top-level JSON value that is not a dict (e.g. a JSON array, string, int, bool, or None) and is not an rpc_chunk dict. Also raised at the end of a chunk reassembly when the fully reassembled frame parses to a non-dict JSON value.

Common situations: Feeding raw lines from a non-omp peer process or socket that emits bare JSON arrays or scalars; piping stdout of a script that logs plain strings; a peer library version that speaks a different (non-object) framing; accidentally passing a parsed JSON document root instead of a single frame.

Related errors


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