{"record":{"id":"2d9e744392e0a884","repo":"can1357/oh-my-pi","slug":"rpc-payload-objects-must-use-string-keys","errorCode":null,"errorMessage":"RPC payload objects must use string keys","messagePattern":"RPC payload objects must use string keys","errorType":"exception","errorClass":"RpcError","httpStatus":null,"severity":"error","filePath":"python/omp-rpc/src/omp_rpc/client.py","lineNumber":291,"sourceCode":"    except subprocess.TimeoutExpired:\n        pass\n    _signal_group(signal.SIGKILL)\n    try:\n        process.wait(timeout=1.0)\n    except subprocess.TimeoutExpired:\n        pass\n\n\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","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/omp-rpc/src/omp_rpc/client.py#L273-L309","documentation":"_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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nparams = {\"todos\": {1: \"write tests\"}}\nawait client.request(\"todo/update\", params)\n\n// after\nparams = {\"todos\": {str(k): v for k, v in {1: \"write tests\"}.items()}}\nawait client.request(\"todo/update\", params)","handlingStrategy":"validation","validationCode":"def ensure_str_keys(value, path=\"$\"):\n    if isinstance(value, dict):\n        for k, v in value.items():\n            if not isinstance(k, str):\n                raise ValueError(f\"non-string key at {path}: {k!r}\")\n            ensure_str_keys(v, f\"{path}.{k}\")\n    elif isinstance(value, list):\n        for i, v in enumerate(value):\n            ensure_str_keys(v, f\"{path}[{i}]\")","typeGuard":"def has_str_keys(d: dict) -> bool:\n    return all(isinstance(k, str) for k in d)","tryCatchPattern":"try:\n    await client.request(method, params)\nexcept RpcError as exc:\n    if \"must use string keys\" in str(exc):\n        params = json.loads(json.dumps(params, default=str))\n        await client.request(method, params)\n    else:\n        raise","preventionTips":["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"],"tags":["rpc","json","payload-validation","type-error"],"backgroundTag":"json-serializable-payload","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}