{"record":{"id":"9f502a480fe16810","repo":"can1357/oh-my-pi","slug":"field-must-be-one-of-expected","errorCode":null,"errorMessage":"{field} must be one of: {expected}","messagePattern":"(.+?) must be one of: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/omp-rpc/src/omp_rpc/protocol.py","lineNumber":191,"sourceCode":"    if values is None:\n        return None\n    if not isinstance(values, list):\n        raise ValueError(f\"{field} must be a list\")\n    return tuple(_clone_json_object(item, field=f\"{field}[]\") for item in values)\n\n\ndef _clone_json_objects(values: object, *, field: str) -> tuple[JsonObject, ...]:\n    if values is None:\n        return ()\n    if not isinstance(values, list):\n        raise ValueError(f\"{field} must be a list\")\n    return tuple(_clone_json_object(item, field=f\"{field}[]\") for item in values)\n\n\ndef _require_literal(value: object, allowed: frozenset[str], *, field: str) -> str:\n    if not isinstance(value, str) or value not in allowed:\n        expected = \", \".join(sorted(allowed))\n        raise ValueError(f\"{field} must be one of: {expected}\")\n    return value\n\n\ndef _optional_literal(\n    value: object, allowed: frozenset[str], *, field: str\n) -> str | None:\n    if value is None:\n        return None\n    return _require_literal(value, allowed, field=field)\n\n\ndef _require_str(payload: JsonObject, field: str) -> str:\n    value = payload.get(field)\n    if not isinstance(value, str):\n        raise ValueError(f\"{field} must be a string\")\n    return value\n\n","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/omp-rpc/src/omp_rpc/protocol.py#L173-L209","documentation":"`_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`.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nparse_todo_item({\"id\": \"1\", \"content\": \"ship\", \"status\": \"done\"})\n// ValueError: status must be one of: abandoned, blocked, completed, in_progress, pending\n\n// after\nparse_todo_item({\"id\": \"1\", \"content\": \"ship\", \"status\": \"completed\"})","handlingStrategy":"validation","validationCode":"_EFFORT = {\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"}\n_TODO_STATUS = {\"pending\", \"in_progress\", \"completed\", \"abandoned\", \"blocked\"}\n_ROLES = {\"user\", \"developer\", \"assistant\", \"toolResult\", \"bashExecution\",\n          \"pythonExecution\", \"custom\", \"hookMessage\", \"branchSummary\",\n          \"compactionSummary\", \"fileMention\"}\n\nassert value in _EFFORT, f\"effort {value!r} not in {sorted(_EFFORT)}\"\nassert status in _TODO_STATUS, f\"status {status!r} not in {sorted(_TODO_STATUS)}\"\nassert role in _ROLES, f\"role {role!r} not in {sorted(_ROLES)}\"","typeGuard":"from typing import TypeGuard\ndef is_allowed_literal(value: object, allowed: frozenset[str]) -> TypeGuard[str]:\n    return isinstance(value, str) and value in allowed","tryCatchPattern":"try:\n    item = parse_todo_item(raw)\nexcept ValueError as exc:\n    if \"must be one of\" in str(exc):\n        allowed = str(exc).split(\": \")[-1].split(\", \")\n        logger.error(\"invalid literal %r for %s; allowed: %s\",\n                     raw.get(field), field, allowed)\n        raise TypeError(exc) from None\n    raise","preventionTips":["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."],"tags":["python","validation","rpc","enum","literal"],"backgroundTag":"invalid-enum-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}