can1357/oh-my-pi · error · ValueError

{field} must be a string or an array of strings

Error message

{field} must be a string or an array of strings

What it means

`_optional_str_list` raises this when the field is present but is neither a string nor a list of strings — e.g. a number, boolean, or object. It is the top-level shape check before per-element validation.

Source

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

    The agent's `systemPrompt` (and similar) became `string[]` server-side
    when multi-prompt support landed. Older daemons still emit a bare string,
    so we accept either shape. Returns an empty tuple when the field is
    absent or null.
    """
    value = payload.get(field)
    if value is None:
        return ()
    if isinstance(value, str):
        return (value,)
    if isinstance(value, list):
        items: list[str] = []
        for index, item in enumerate(value):
            if not isinstance(item, str):
                raise ValueError(f"{field}[{index}] must be a string")
            items.append(item)
        return tuple(items)
    raise ValueError(f"{field} must be a string or an array of strings")


def _optional_bool(payload: JsonObject, field: str) -> bool | None:
    value = payload.get(field)
    if value is None:
        return None
    if not isinstance(value, bool):
        raise ValueError(f"{field} must be a boolean")
    return value


def _optional_int(payload: JsonObject, field: str) -> int | None:
    value = payload.get(field)
    if value is None:
        return None
    if isinstance(value, bool) or not isinstance(value, int):
        raise ValueError(f"{field} must be an integer")
    return value

View on GitHub (pinned to 9690622007)

Solutions

  1. Print the payload field and confirm its type; wrap scalars in a list or fix the shape
  2. Convert dicts to their keys/values as a string list if that is the intended semantics
  3. Update client and server to matching protocol versions

Example fix

# before
payload = {"tags": {"a": 1}}
state = parse_session_state(payload)  # ValueError: tags must be a string or an array of strings
# after
payload = {"tags": list({"a": 1}.keys())}
state = parse_session_state(payload)
Defensive patterns

Strategy: validation

Validate before calling

def ensure_str_or_str_list(payload: dict, field: str) -> None:
    value = payload.get(field)
    if value is None:
        return
    if isinstance(value, str):
        return
    if isinstance(value, list) and all(isinstance(x, str) for x in value):
        return
    raise TypeError(f"{field!r} must be a string or list of strings, got {value!r}")

ensure_str_or_str_list(payload, "tags")
parse_session_state(payload)

Type guard

def is_str_or_str_list(value: object) -> bool:
    if isinstance(value, str):
        return True
    return isinstance(value, list) and all(isinstance(x, str) for x in value)

Try / catch

try:
    state = parse_session_state(payload)
except ValueError as e:
    logger.error("session state shape mismatch", extra={"payload": payload, "error": str(e)})
    raise ProtocolError("incompatible session state payload") from e

Prevention

When it happens

Trigger: parse_session_state receives an optional string-list field whose value is an int, dict, or other structure — e.g. `"tags": {"a": 1}` or `"tags": 7`.

Common situations: A schema change turned a string field into an object; the producer sent a single dict instead of a list; a client bug passed the wrong key's value into the payload.

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/36e95897148b6243. Report an issue: GitHub.