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
- Verify the sender emits one JSON object per frame (e.g. {"type": "request", ...}), not arrays or scalars.
- Inspect what you feed push(): if you parse lines yourself, ensure each line is json.loads'ed to a dict before pushing.
- If the peer legitimately streams a top-level array, iterate its elements and push each object individually.
- Check peer/library version compatibility with the omp-rpc framing protocol.
- 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
- Type-check frames with isinstance(value, dict) before push().
- Only feed push() with frames produced by json.loads of trusted single-object lines.
- Unit-test your frame pump against arrays/scalars/null inputs.
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
- invalid rpc chunk data
- rpc frame must be an object
- invalid rpc chunk metadata
- rpc chunk payload exceeds the transport limit
- rpc chunk sequence exceeds declared length
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8fc5409af5ee087b.
Report an issue: GitHub.