can1357/oh-my-pi · error · RpcError
Failed to decode reassembled RPC frame
Error message
Failed to decode reassembled RPC frame
What it means
The chunk sequence completed and the byte counts matched, but the reassembled bytes could not be decoded as UTF-8 or parsed as JSON. Chunk-level validation only guarantees base64 integrity and length consistency — it cannot guarantee the sender encoded valid UTF-8 JSON. The library wraps the underlying UnicodeDecodeError/JSONDecodeError in a domain RpcError.
Source
Thrown at python/omp-rpc/src/omp_rpc/client.py:213
or pending.next_index != index
):
raise RpcError("RPC chunk sequence mismatch")
pending.chunks.append(chunk)
pending.received_bytes += len(chunk)
pending.next_index += 1
if pending.received_bytes > pending.byte_length:
raise RpcError("RPC chunk sequence exceeds its declared length")
if pending.next_index < pending.count:
return None
if pending.received_bytes != pending.byte_length:
raise RpcError("RPC chunk sequence length mismatch")
self._pending = None
try:
decoded = b"".join(pending.chunks).decode("utf-8")
frame = json.loads(decoded)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RpcError("Failed to decode reassembled RPC frame") from exc
if not isinstance(frame, dict):
raise RpcError("RPC frame must be a JSON object")
return cast(JsonObject, frame)
def _process_group_id(process: subprocess.Popen[Any]) -> int | None:
"""Process-group id of `process`, or `None` when groups are unavailable.
Captured right after spawn so teardown can signal the whole group even
after the leader is reaped — POSIX `os.getpgid` fails on a reaped pid.
"""
getpgid = getattr(os, "getpgid", None)
if getpgid is None:
return None
try:
return getpgid(process.pid)
except OSError:
return NoneView on GitHub (pinned to 9690622007)
Solutions
- On the sender, chunk the payload after encoding: payload_bytes = json.dumps(frame).encode('utf-8'), and split those bytes so multi-byte chars are never severed.
- Ensure exactly one JSON object is serialized per sequence (json.dumps of one dict).
- If the payload is binary, use the appropriate binary transport instead of the JSON chunking path.
- Inspect the reassembled bytes by decoding leniently on the sender side (errors='replace') to find the corruption point.
- Verify sender locale/encoding (PYTHONIOENCODING, open() encoding) forces UTF-8.
Example fix
// before
text = json.dumps(frame)
chunks = [text[i:i+n] for i in range(0, len(text), n)] # may split multibyte chars
// after
encoded = json.dumps(frame).encode("utf-8")
chunks = [encoded[i:i+n] for i in range(0, len(encoded), n)] # byte-safe
send_chunks(chunks, byteLength=len(encoded)) Defensive patterns
Strategy: try-catch
Validate before calling
import json
def payload_is_valid_json(payload: bytes) -> bool:
try:
json.loads(payload.decode("utf-8"))
return True
except (UnicodeDecodeError, json.JSONDecodeError):
return False
# run on the sender's full payload before chunking Type guard
def is_utf8_json_dict(payload: bytes) -> TypeGuard[bytes]:
try:
import json
return isinstance(json.loads(payload.decode("utf-8")), dict)
except Exception:
return False Try / catch
try:
frame = client.push(last_chunk)
except RpcError as exc:
if str(exc) == "Failed to decode reassembled RPC frame":
logger.error("reassembled payload is not UTF-8 JSON; dumping hex for diagnosis")
dump_reassembled_hex_for_debug()
request_resend()
else:
raise Prevention
- Serialize with json.dumps(...).encode('utf-8') and chunk the bytes, never the str.
- Validate the full payload parses as one JSON object before chunking it on the sender.
- Force UTF-8 everywhere (file opens, subprocess env, network codecs).
- Route binary payloads through a binary path, not the JSON chunking protocol.
When it happens
Trigger: push() completing a chunk sequence whose concatenated bytes are not valid UTF-8, or valid UTF-8 that is not parseable JSON (truncated JSON, concatenated objects, binary data, wrong character encoding like latin-1).
Common situations: Sender serialized with a non-UTF-8 codec; payload was sliced mid-multi-byte-character because chunks were cut by character count; the 'frame' was actually binary (e.g. protobuf/msgpack) sent through the JSON chunking path; sender wrote multiple JSON documents concatenated instead of one.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid RPC chunk data
- RPC chunk sequence exceeds its declared length
- Replacement text is not valid UTF-8: {err}
- rpc frame must be an object
- rpc chunk sequence length mismatch
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a65ed1067ac95b4d.
Report an issue: GitHub.