can1357/oh-my-pi · error · ValueError

{field} must be a string

Error message

{field} must be a string

What it means

omp-rpc's protocol parser validates every field of an incoming JSON payload before constructing typed objects. `_require_str` throws this ValueError when a required field named `{field}` is missing or is not a JSON string. The library fails fast so malformed payloads surface at parse time instead of causing type errors deep in application code.

Source

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

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


def _optional_literal(
    value: object, allowed: frozenset[str], *, field: str
) -> str | None:
    if value is None:
        return None
    return _require_literal(value, allowed, field=field)


def _require_str(payload: JsonObject, field: str) -> str:
    value = payload.get(field)
    if not isinstance(value, str):
        raise ValueError(f"{field} must be a string")
    return value


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


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

View on GitHub (pinned to 9690622007)

Solutions

  1. Log/print the full payload and check the named field's actual JSON type before calling the parser
  2. Coerce or fix the value to a string (str(value) or correct the producer)
  3. If the field is legitimately absent, use the optional variant or supply a default before parsing
  4. Pin both client and server to compatible omp-rpc versions so the wire schema matches

Example fix

# before
payload = {"id": 42, "name": "bash"}
info = parse_tool_descriptor(payload)  # ValueError: id must be a string
# after
payload = {"id": str(42), "name": "bash"}
info = parse_tool_descriptor(payload)
Defensive patterns

Strategy: validation

Validate before calling

def ensure_str(payload: dict, field: str) -> None:
    value = payload.get(field)
    if not isinstance(value, str):
        raise TypeError(f"{field!r} must be a string, got {type(value).__name__}: {value!r}")

# before parsing:
ensure_str(payload, "id")
parse_tool_descriptor(payload)

Type guard

def is_str_field(payload: dict, field: str) -> bool:
    return isinstance(payload.get(field), str)

Try / catch

try:
    info = parse_tool_descriptor(payload)
except ValueError as e:
    logger.error("invalid tool descriptor payload", extra={"payload": payload, "error": str(e)})
    raise ProtocolError("malformed tool descriptor") from e

Prevention

When it happens

Trigger: Calling parse_model_info, parse_tool_descriptor, parse_todo_item, parse_todo_phase, parse_session_state, or _parse_thinking_config with a payload whose required field is absent (None), a number, bool, list, or dict instead of a string — e.g. a deserialized RPC response missing `id` or carrying `id: 123` instead of `"123"`.

Common situations: A server upgraded and renamed/retyped a field; a hand-built fixture or mock response uses a numeric ID; JSON was produced by a non-Python serializer that emits unquoted values; a partially-constructed dict was passed to the parser.

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/3443313a66f0dcd7. Report an issue: GitHub.