can1357/oh-my-pi · error · ValueError
{field} must be an object
Error message
{field} must be an object What it means
`_clone_json_object` is the gatekeeper for every field the protocol expects to be a JSON object. It raises this ValueError when the supplied value is not a `dict` (e.g. it is a list, string, or number), before any deep cloning happens. This keeps malformed RPC payloads from being accepted into typed protocol structures.
Source
Thrown at python/omp-rpc/src/omp_rpc/protocol.py:160
def _clone_json_value(value: object, *, field: str) -> 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, field=field) for item in value]
if isinstance(value, dict):
cloned: JsonObject = {}
for key, item in value.items():
if not isinstance(key, str):
raise ValueError(f"{field} must contain string keys")
cloned[key] = _clone_json_value(item, field=field)
return cloned
raise ValueError(f"{field} must be JSON-serializable")
def _clone_json_object(value: object, *, field: str) -> JsonObject:
if not isinstance(value, dict):
raise ValueError(f"{field} must be an object")
return cast(JsonObject, _clone_json_value(value, field=field))
def _optional_json_object(value: object, *, field: str) -> JsonObject | None:
if value is None:
return None
return _clone_json_object(value, field=field)
def _optional_json_objects(
values: object, *, field: str
) -> tuple[JsonObject, ...] | None:
if values is None:
return None
if not isinstance(values, list):
raise ValueError(f"{field} must be a list")
return tuple(_clone_json_object(item, field=f"{field}[]") for item in values)
View on GitHub (pinned to 9690622007)
Solutions
- Check the value's type at the call site and ensure it is a dict (`isinstance(value, dict)`); `json.loads` any JSON-string payloads before passing them.
- If the value can be absent, use the `_optional_json_object`-backed API or pass None explicitly instead of an empty non-dict value.
- Verify client/daemon protocol versions match — a schema drift may have changed this field's shape.
- Log/repr the raw payload field to confirm its actual shape before parsing.
Example fix
// before
raw = '{"options": {"timeout": 5}}'
parse_extension_ui_request({"method": "select", "options": raw})
// ValueError: options must be an object
// after
import json
parse_extension_ui_request({"method": "select", "options": json.loads(raw)}) Defensive patterns
Strategy: type-guard
Validate before calling
def is_json_object(value) -> bool:
return isinstance(value, dict) and all(isinstance(k, str) for k in value)
if not is_json_object(payload.get(field)):
raise TypeError(f"{field} must be a JSON object, got {type(value).__name__}") Type guard
def is_json_object(value: object) -> bool:
return isinstance(value, dict) Try / catch
try:
msg = parse_agent_messages(raw)
except ValueError as exc:
if "must be an object" in str(exc):
logger.warning("malformed payload for %s: %r", field, raw.get(field))
msg = fallback_message(raw)
else:
raise Prevention
- Always json.loads JSON-encoded strings before embedding them as nested objects.
- Use the *_optional* parse helpers when a field may legitimately be absent.
- Pin and align client/daemon protocol versions.
- Validate payloads with a JSON schema before handing them to the parser.
When it happens
Trigger: Passing a non-dict where an object field is expected: e.g. `_optional_json_object`/`parse_agent_messages`/`parse_assistant_message_event` receiving a JSON array or a JSON-encoded string like '{"a":1}' that was never parsed, or a field that is None being routed through the required variant `_clone_json_objects` items.
Common situations: Double-encoding a nested payload as a JSON string and forgetting `json.loads`; the server/API changed a field from object to list (version mismatch between client and daemon); using `payload.get('x')` and passing a list where a dict belongs; typos so a sibling field is passed as the object.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- {field} must be a list
- RPC frame must be a JSON object
- {field} must be JSON-serializable
- {field} must be one of: {expected}
- Unsupported language '{value}'. Supported: {}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d944d5dc81fa2849.
Report an issue: GitHub.