can1357/oh-my-pi · error · RpcError

RPC response payload must be an object

Error message

RPC response payload must be an object

What it means

_clone_json_object validates that an incoming RPC response/result payload is a JSON object (dict) before cloning it. It is called on response paths (_request, stdout loop events, protocol-error building), so a server response whose payload is an array, string, or null triggers this RpcError.

Source

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

def _clone_json_value(value: object) -> JsonValue:
    if value is None or isinstance(value, (str, int, float, bool)):
        return cast(JsonValue, value)
    if isinstance(value, list):
        return [_clone_json_value(item) for item in value]
    if isinstance(value, dict):
        cloned: JsonObject = {}
        for key, item in value.items():
            if not isinstance(key, str):
                raise RpcError("RPC payload objects must use string keys")
            cloned[key] = _clone_json_value(item)
        return cloned
    raise RpcError("RPC payload must be JSON-serializable")


def _clone_json_object(value: object) -> JsonObject:
    if not isinstance(value, dict):
        raise RpcError("RPC response payload must be an object")
    return cast(JsonObject, _clone_json_value(value))


class RpcError(RuntimeError):
    """Base exception for the Python RPC client."""


class RpcTimeoutError(RpcError):
    """Raised when the server does not respond before a timeout."""


class RpcProcessExitError(RpcError):
    """Raised when the RPC process exits while a request is pending."""


class RpcConcurrencyError(RpcError):
    """Raised when overlapping prompt lifecycle collectors would be ambiguous."""

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify server and client protocol versions match; align the response schema to one that returns objects.
  2. Inspect raw frames (stderr/logs) to see the actual payload shape being returned.
  3. If you control the server, wrap results in an object: {"items": [...] } instead of [...].
  4. Check for intermediaries (proxies, logging shims) transforming the response.

Example fix

// before: server returns a bare array
result = [1, 2, 3]

// after: return an object envelope
result = {"items": [1, 2, 3]}
Defensive patterns

Strategy: try-catch

Type guard

def is_object_payload(payload) -> bool:
    return isinstance(payload, dict)

Try / catch

try:
    result = await client.request(method, params)
except RpcError as exc:
    if "response payload must be an object" in str(exc):
        logger.error("server returned non-object payload; check protocol version")
    else:
        raise

Prevention

When it happens

Trigger: The server replies with a non-object result/event payload, e.g. a bare array or null where the protocol expects an object, and the client clones it in _request or _read_stdout_loop.

Common situations: Version mismatch where an older/newer server returns a different response shape; a proxy or shim rewriting responses; custom agent returning a plain list as the result field.

Related errors


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