can1357/oh-my-pi · error · ValueError
{field} must be one of: {expected}
Error message
{field} must be one of: {expected} What it means
`_require_literal` enforces enum-like string fields: the value must be a `str` and a member of the allowed frozenset, otherwise the error lists the accepted values (sorted). It backs typed literals such as message roles, thinking levels/efforts, todo statuses, stop reasons, and session states used by `_parse_agent_message`, `parse_assistant_message_event`, `_parse_thinking_config`, `parse_todo_item`, and `parse_session_state`.
Source
Thrown at python/omp-rpc/src/omp_rpc/protocol.py:191
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
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
View on GitHub (pinned to 9690622007)
Solutions
- Read the allowed values listed in the error message and change your payload to exactly one of them (case-sensitive).
- If using an Enum, pass `my_enum.value` rather than the enum instance so it arrives as a plain str.
- Cross-check your field values against the Literal type aliases at the top of omp_rpc/protocol.py (e.g. Effort, ThinkingLevel, TodoStatus) for the version you are running.
- Check for client/daemon version drift: renamed literal values require updating the sender to the new vocabulary.
Example fix
// before
parse_todo_item({"id": "1", "content": "ship", "status": "done"})
// ValueError: status must be one of: abandoned, blocked, completed, in_progress, pending
// after
parse_todo_item({"id": "1", "content": "ship", "status": "completed"}) Defensive patterns
Strategy: validation
Validate before calling
_EFFORT = {"minimal", "low", "medium", "high", "xhigh", "max"}
_TODO_STATUS = {"pending", "in_progress", "completed", "abandoned", "blocked"}
_ROLES = {"user", "developer", "assistant", "toolResult", "bashExecution",
"pythonExecution", "custom", "hookMessage", "branchSummary",
"compactionSummary", "fileMention"}
assert value in _EFFORT, f"effort {value!r} not in {sorted(_EFFORT)}"
assert status in _TODO_STATUS, f"status {status!r} not in {sorted(_TODO_STATUS)}"
assert role in _ROLES, f"role {role!r} not in {sorted(_ROLES)}" Type guard
from typing import TypeGuard
def is_allowed_literal(value: object, allowed: frozenset[str]) -> TypeGuard[str]:
return isinstance(value, str) and value in allowed Try / catch
try:
item = parse_todo_item(raw)
except ValueError as exc:
if "must be one of" in str(exc):
allowed = str(exc).split(": ")[-1].split(", ")
logger.error("invalid literal %r for %s; allowed: %s",
raw.get(field), field, allowed)
raise TypeError(exc) from None
raise Prevention
- Use Python Enum / Literal types at the call site and send `.value` strings, never enum instances.
- Keep sender-side vocabularies in sync with the Literal aliases in protocol.py for your installed version.
- Watch case sensitivity: values like 'toolResult' and 'in_progress' are exact.
- Write a unit test per enum-ish field asserting each accepted value parses.
When it happens
Trigger: Supplying an unrecognized or non-string value for a Literal field, e.g. role 'system' (not in the accepted roles set), effort 'ultra' instead of one of minimal|low|medium|high|xhigh|max, todo status 'done' instead of 'completed', or passing an enum member object instead of its `.value` string.
Common situations: Using a role or status name from a different API version or another library's enum; passing Python `Enum` instances instead of plain strings; typos or case mistakes ('High' vs 'high'); a protocol update that renamed a literal value while your client still sends the old one.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Unsupported language '{value}'. Supported: {}
- RPC frame must be a JSON object
- Unsupported todo status: {seed.status}
- {field} must be JSON-serializable
- {field} must be an object
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/9f502a480fe16810.
Report an issue: GitHub.