can1357/oh-my-pi · error · RpcError
RPC payload must be JSON-serializable
Error message
RPC payload must be JSON-serializable
What it means
_clone_json_value raises this when it encounters a value that is not JSON-serializable (dict, list, str, int, float, bool, None) while deep-copying an RPC payload. It enforces strict JSON semantics before sending so the wire format is never invalid.
Source
Thrown at python/omp-rpc/src/omp_rpc/client.py:294
try:
process.wait(timeout=1.0)
except subprocess.TimeoutExpired:
pass
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."""View on GitHub (pinned to 9690622007)
Solutions
- Convert non-JSON values before sending: isoformat() for datetimes, sorted(list) for sets, str(path) for Paths.
- Use json.loads(json.dumps(payload, default=str)) as a normalization step, or add a default serializer.
- Pre-validate the payload with json.dumps(payload) in a try/except before making the call to localize the offending field.
- Add explicit serialization helpers at the boundary where payload data is constructed.
Example fix
// before
params = {"due": datetime.now(), "tags": {"a", "b"}}
await client.prompt(params)
// after
params = {"due": datetime.now().isoformat(), "tags": sorted({"a", "b"})}
await client.prompt(params) Defensive patterns
Strategy: validation
Validate before calling
def assert_json_serializable(payload):
json.dumps(payload) # raises TypeError for datetime/set/bytes/etc. Type guard
def is_json_value(v) -> bool:
if v is None or isinstance(v, (str, bool, int, float)):
return True
if isinstance(v, dict):
return all(isinstance(k, str) and is_json_value(x) for k, x in v.items())
if isinstance(v, list):
return all(is_json_value(x) for x in v)
return False Try / catch
try:
await client.prompt(params)
except RpcError as exc:
if "JSON-serializable" in str(exc):
params = json.loads(json.dumps(params, default=str))
await client.prompt(params)
else:
raise Prevention
- Convert datetimes with .isoformat(), sets with sorted(), Paths with str()
- Never put ORM/model instances directly into params
- Run a json.dumps smoke test on payloads in tests
When it happens
Trigger: Passing objects like datetime, set, bytes, custom class instances, pathlib.Path, or Decimal inside request params to any client method that sends a payload.
Common situations: Including datetime.now() timestamps, sets of tags, or ORM model objects directly in params; passing Path objects from file-handling code into RPC arguments.
Related errors
- RPC payload objects must use string keys
- Replacement text is not valid UTF-8: {err}
- rpc frame must be an object
- rpc chunk sequence length mismatch
- Value is not JSON-serializable
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/57442155af12940e.
Report an issue: GitHub.