{"record":{"id":"deb51852f1f393ab","repo":"can1357/oh-my-pi","slug":"field-index-must-be-a-string","errorCode":null,"errorMessage":"{field}[{index}] must be a string","messagePattern":"(.+?)\\[(.+?)\\] must be a string","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/omp-rpc/src/omp_rpc/protocol.py","lineNumber":243,"sourceCode":"\ndef _optional_str_list(payload: JsonObject, field: str) -> tuple[str, ...]:\n    \"\"\"Parse an optional string-or-array-of-strings field.\n\n    The agent's `systemPrompt` (and similar) became `string[]` server-side\n    when multi-prompt support landed. Older daemons still emit a bare string,\n    so we accept either shape. Returns an empty tuple when the field is\n    absent or null.\n    \"\"\"\n    value = payload.get(field)\n    if value is None:\n        return ()\n    if isinstance(value, str):\n        return (value,)\n    if isinstance(value, list):\n        items: list[str] = []\n        for index, item in enumerate(value):\n            if not isinstance(item, str):\n                raise ValueError(f\"{field}[{index}] must be a string\")\n            items.append(item)\n        return tuple(items)\n    raise ValueError(f\"{field} must be a string or an array of strings\")\n\n\ndef _optional_bool(payload: JsonObject, field: str) -> bool | None:\n    value = payload.get(field)\n    if value is None:\n        return None\n    if not isinstance(value, bool):\n        raise ValueError(f\"{field} must be a boolean\")\n    return value\n\n\ndef _optional_int(payload: JsonObject, field: str) -> int | None:\n    value = payload.get(field)\n    if value is None:\n        return None","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/omp-rpc/src/omp_rpc/protocol.py#L225-L261","documentation":"`_optional_str_list` accepts a field that is absent, a single string, or an array of strings. When an array is supplied, every element must be a string; this error fires naming the offending index, reporting exactly which element was invalid (`field[index]`).","triggerScenarios":"parse_session_state receives a list field where one element is a number, null, bool, or nested object — e.g. `[\"a\", 3, \"b\"]` fails at index 1.","commonSituations":"A producer mixes numeric and string IDs in an array; JSON from another language serializes mixed-type lists; a fixture was hand-edited and one entry lost its quotes.","solutions":["Look at the index in the message, coerce that element to a string, and re-parse","Normalize the array before parsing: [str(x) for x in value]","Fix the producer so every element is a JSON string"],"exampleFix":"# before\npayload = {\"todos\": [\"a\", 2, \"b\"]}\nstate = parse_session_state(payload)  # ValueError: todos[1] must be a string\n# after\npayload = {\"todos\": [str(x) for x in [\"a\", 2, \"b\"]]}\nstate = parse_session_state(payload)","handlingStrategy":"validation","validationCode":"def ensure_str_list_items(payload: dict, field: str) -> None:\n    value = payload.get(field)\n    if isinstance(value, list):\n        for i, item in enumerate(value):\n            if not isinstance(item, str):\n                raise TypeError(f\"{field}[{i}] must be a string, got {item!r}\")\n\nensure_str_list_items(payload, \"todos\")\nparse_session_state(payload)","typeGuard":"def is_str_or_str_list(value: object) -> bool:\n    if isinstance(value, str):\n        return True\n    return isinstance(value, list) and all(isinstance(x, str) for x in value)","tryCatchPattern":"try:\n    state = parse_session_state(payload)\nexcept ValueError as e:\n    if \"must be a string\" in str(e):\n        field = str(e).split(\"[\")[0]\n        payload[field] = [str(x) for x in payload.get(field, [])]\n        state = parse_session_state(payload)\n    else:\n        raise","preventionTips":["Normalize arrays to strings at the producer boundary","Avoid mixed-type lists in wire protocols","Add per-element schema validation for list fields","Round-trip test fixtures through the parser in CI"],"tags":["python","rpc","type-validation","array"],"backgroundTag":"schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}