can1357/oh-my-pi · error · ValueError

{field} must be a list

Error message

{field} must be a list

What it means

`_optional_json_objects` parses an optional field that must be a JSON array of objects, returning None when the value is None. It raises this ValueError when the value is present but is not a list — commonly because a single object was supplied instead of wrapping it in an array. Items are additionally validated as objects via `_clone_json_object` with field name `{field}[]`.

Source

Thrown at python/omp-rpc/src/omp_rpc/protocol.py:176

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)


def _clone_json_objects(values: object, *, field: str) -> tuple[JsonObject, ...]:
    if values is None:
        return ()
    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)


def _require_literal(value: object, allowed: frozenset[str], *, field: str) -> str:
    if not isinstance(value, str) or value not in allowed:
        expected = ", ".join(sorted(allowed))
        raise ValueError(f"{field} must be one of: {expected}")
    return value

View on GitHub (pinned to 9690622007)

Solutions

  1. Wrap single objects in a list before parsing: pass `[obj]` instead of `obj`.
  2. If the value may be absent, pass None (or omit it) rather than an empty dict, so the optional path returns None cleanly.
  3. Compare the payload shape against the current protocol schema in omp_rpc/protocol.py to confirm the expected `list[object]` shape.
  4. Check for client/daemon version mismatch if the field shape recently changed upstream.

Example fix

// before
parse_extension_ui_request({"method": "select", "widgets": {"id": 1}})
// ValueError: widgets must be a list

// after
parse_extension_ui_request({"method": "select", "widgets": [{"id": 1}]})
Defensive patterns

Strategy: type-guard

Validate before calling

def is_json_object_list(value) -> bool:
    return value is None or (
        isinstance(value, list)
        and all(isinstance(item, dict) for item in value)
    )

assert is_json_object_list(payload.get(field)), f"{field} must be a list of objects"

Type guard

def is_object_list(value: object) -> bool:
    return isinstance(value, list) and all(isinstance(item, dict) for item in value)

Try / catch

try:
    req = parse_extension_ui_request(raw)
except ValueError as exc:
    if "must be a list" in str(exc):
        # auto-wrap a lone object if that's the common caller mistake
        raw[field] = [raw[field]] if isinstance(raw.get(field), dict) else []
        req = parse_extension_ui_request(raw)
    else:
        raise

Prevention

When it happens

Trigger: `parse_extension_ui_request` receiving e.g. `"options": {"a": 1}` (a single object) or `"options": "a,b"` (a string) where a list of objects is required: `[{'a': 1}]`. Also occurs when the field is accidentally omitted-safe None but a truthy non-list like a dict is supplied.

Common situations: Client sends one element without array brackets; a config file specifies a map instead of a list; an older daemon/client version uses a different shape for the field; hand-written payloads in tests forgot the list wrapper.

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/78020bf3cc05cf1e. Report an issue: GitHub.