can1357/oh-my-pi · error · RpcError
RPC payload objects must use string keys
Error message
RPC payload objects must use string keys
What it means
_clone_json_value deep-copies outgoing payload structures and validates them as proper JSON. Python dicts may have non-string keys (int, bool, None, tuple); JSON objects require string keys, so this RpcError is raised during payload cloning before serialization.
Source
Thrown at python/omp-rpc/src/omp_rpc/client.py:291
except subprocess.TimeoutExpired:
pass
_signal_group(signal.SIGKILL)
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."""
View on GitHub (pinned to 9690622007)
Solutions
- Stringify all dict keys before passing payloads to the client: {str(k): v for k, v in d.items()}.
- Represent keyed data as a list of {"key": ..., "value": ...} objects instead of a mapping with non-string keys.
- Pre-validate payload structure with a recursive JSON-key check in your own code.
- If keys come from an upstream source (config file, DB), normalize them at ingestion time.
Example fix
// before
params = {"todos": {1: "write tests"}}
await client.request("todo/update", params)
// after
params = {"todos": {str(k): v for k, v in {1: "write tests"}.items()}}
await client.request("todo/update", params) Defensive patterns
Strategy: validation
Validate before calling
def ensure_str_keys(value, path="$"):
if isinstance(value, dict):
for k, v in value.items():
if not isinstance(k, str):
raise ValueError(f"non-string key at {path}: {k!r}")
ensure_str_keys(v, f"{path}.{k}")
elif isinstance(value, list):
for i, v in enumerate(value):
ensure_str_keys(v, f"{path}[{i}]") Type guard
def has_str_keys(d: dict) -> bool:
return all(isinstance(k, str) for k in d) Try / catch
try:
await client.request(method, params)
except RpcError as exc:
if "must use string keys" in str(exc):
params = json.loads(json.dumps(params, default=str))
await client.request(method, params)
else:
raise Prevention
- Build params only from JSON-native literals
- Stringify keys at the boundary when importing data from YAML/DB
- Represent keyed records as lists of objects, not dicts with non-string keys
When it happens
Trigger: Passing a request payload/params containing a dict with non-string keys, e.g. {1: "a"} or {None: "x"}, to a client request such as prompt/params construction.
Common situations: Building params programmatically with integer IDs as keys, converting from formats that allow non-string keys (YAML, SQLite rows), or naive reuse of in-memory caches keyed by objects as the payload itself.
Related errors
- RPC payload must be JSON-serializable
- Invalid pattern: {err}
- Replacement text is not valid UTF-8: {err}
- rpc frame must be an object
- Value is not JSON-serializable
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2d9e744392e0a884.
Report an issue: GitHub.