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 None

View on GitHub (pinned to 9690622007)

Solutions

  1. 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.
  2. Ensure exactly one JSON object is serialized per sequence (json.dumps of one dict).
  3. If the payload is binary, use the appropriate binary transport instead of the JSON chunking path.
  4. Inspect the reassembled bytes by decoding leniently on the sender side (errors='replace') to find the corruption point.
  5. 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

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

Related errors


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