{"record":{"id":"d02c1d16b9308e50","repo":"can1357/oh-my-pi","slug":"rpc-response-payload-must-be-an-object","errorCode":null,"errorMessage":"RPC response payload must be an object","messagePattern":"RPC response payload must be an object","errorType":"exception","errorClass":"RpcError","httpStatus":null,"severity":"error","filePath":"python/omp-rpc/src/omp_rpc/client.py","lineNumber":299,"sourceCode":"\ndef _clone_json_value(value: object) -> JsonValue:\n    if value is None or isinstance(value, (str, int, float, bool)):\n        return cast(JsonValue, value)\n    if isinstance(value, list):\n        return [_clone_json_value(item) for item in value]\n    if isinstance(value, dict):\n        cloned: JsonObject = {}\n        for key, item in value.items():\n            if not isinstance(key, str):\n                raise RpcError(\"RPC payload objects must use string keys\")\n            cloned[key] = _clone_json_value(item)\n        return cloned\n    raise RpcError(\"RPC payload must be JSON-serializable\")\n\n\ndef _clone_json_object(value: object) -> JsonObject:\n    if not isinstance(value, dict):\n        raise RpcError(\"RPC response payload must be an object\")\n    return cast(JsonObject, _clone_json_value(value))\n\n\nclass RpcError(RuntimeError):\n    \"\"\"Base exception for the Python RPC client.\"\"\"\n\n\nclass RpcTimeoutError(RpcError):\n    \"\"\"Raised when the server does not respond before a timeout.\"\"\"\n\n\nclass RpcProcessExitError(RpcError):\n    \"\"\"Raised when the RPC process exits while a request is pending.\"\"\"\n\n\nclass RpcConcurrencyError(RpcError):\n    \"\"\"Raised when overlapping prompt lifecycle collectors would be ambiguous.\"\"\"\n","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/omp-rpc/src/omp_rpc/client.py#L281-L317","documentation":"_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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify server and client protocol versions match; align the response schema to one that returns objects.","Inspect raw frames (stderr/logs) to see the actual payload shape being returned.","If you control the server, wrap results in an object: {\"items\": [...] } instead of [...].","Check for intermediaries (proxies, logging shims) transforming the response."],"exampleFix":"// before: server returns a bare array\nresult = [1, 2, 3]\n\n// after: return an object envelope\nresult = {\"items\": [1, 2, 3]}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def is_object_payload(payload) -> bool:\n    return isinstance(payload, dict)","tryCatchPattern":"try:\n    result = await client.request(method, params)\nexcept RpcError as exc:\n    if \"response payload must be an object\" in str(exc):\n        logger.error(\"server returned non-object payload; check protocol version\")\n    else:\n        raise","preventionTips":["Pin client and server to compatible protocol versions","Ensure server results are always JSON objects","Log raw frames when integrating a new server"],"tags":["rpc","protocol","response-validation","json"],"backgroundTag":"rpc-response-shape-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}