can1357/oh-my-pi · error · ValueError

{field} must contain only strings

Error message

{field} must contain only strings

What it means

After confirming the field is a list, `_tuple_of_strings` checks each element is a string. This error fires when at least one element is a number, bool, null, or nested structure; the parser aborts instead of returning a partially-converted tuple.

Source

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

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


def _tuple_of_strings(values: object, *, field: str) -> tuple[str, ...] | None:
    if values is None:
        return None
    if not isinstance(values, list):
        raise ValueError(f"{field} must be a list")

    result: list[str] = []
    for item in values:
        if not isinstance(item, str):
            raise ValueError(f"{field} must contain only strings")
        result.append(item)
    return tuple(result) or None


def _parse_agent_message(payload: JsonObject, *, field: str) -> AgentMessage:
    _require_literal(
        payload.get("role"), _AGENT_MESSAGE_ROLE_VALUES, field=f"{field}.role"
    )
    return cast(AgentMessage, _clone_json_object(payload, field=field))


def _parse_assistant_message(payload: JsonObject, *, field: str) -> AssistantMessage:
    message = _parse_agent_message(payload, field=field)
    if message.get("role") != "assistant":
        raise ValueError(f"{field}.role must be 'assistant'")
    return cast(AssistantMessage, message)

View on GitHub (pinned to 9690622007)

Solutions

  1. Map elements to strings before parsing: [str(x) for x in value] — verify str() preserves wire semantics (numeric enums may need a lookup table)
  2. Fix the producer so the list contains only JSON strings
  3. Drop None entries if they are placeholders: [x for x in value if x is not None]

Example fix

# before
payload = {"modes": ["text", 2]}
info = parse_model_info(payload)  # ValueError: modes must contain only strings
# after
payload = {"modes": [str(x) for x in ["text", 2]]}
info = parse_model_info(payload)
Defensive patterns

Strategy: validation

Validate before calling

def ensure_list_of_str(payload: dict, field: str) -> None:
    value = payload.get(field)
    if value is None:
        return
    if not isinstance(value, list):
        raise TypeError(f"{field!r} must be a list")
    for item in value:
        if not isinstance(item, str):
            raise TypeError(f"{field!r} contains non-string: {item!r}")

ensure_list_of_str(payload, "modes")
parse_model_info(payload)

Type guard

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

Try / catch

try:
    req = parse_extension_ui_request(payload)
except ValueError as e:
    logger.error("string-list field had mixed types", extra={"payload": payload, "error": str(e)})
    raise ProtocolError("malformed extension request") from e

Prevention

When it happens

Trigger: parse_model_info or parse_extension_ui_request receives a list field containing mixed types — e.g. ["text", 2] or [None] in a field like supported input modes or argument names.

Common situations: Numeric enum values sent where string enums are expected; a producer includes null placeholders in lists; mixed-type arrays from dynamically typed producer code.

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/267c3c64c3d8b92b. Report an issue: GitHub.