{"record":{"id":"36e95897148b6243","repo":"can1357/oh-my-pi","slug":"field-must-be-a-string-or-an-array-of-strings","errorCode":null,"errorMessage":"{field} must be a string or an array of strings","messagePattern":"(.+?) must be a string or an array of strings","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/omp-rpc/src/omp_rpc/protocol.py","lineNumber":246,"sourceCode":"\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\n    if isinstance(value, bool) or not isinstance(value, int):\n        raise ValueError(f\"{field} must be an integer\")\n    return value","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/omp-rpc/src/omp_rpc/protocol.py#L228-L264","documentation":"`_optional_str_list` raises this when the field is present but is neither a string nor a list of strings — e.g. a number, boolean, or object. It is the top-level shape check before per-element validation.","triggerScenarios":"parse_session_state receives an optional string-list field whose value is an int, dict, or other structure — e.g. `\"tags\": {\"a\": 1}` or `\"tags\": 7`.","commonSituations":"A schema change turned a string field into an object; the producer sent a single dict instead of a list; a client bug passed the wrong key's value into the payload.","solutions":["Print the payload field and confirm its type; wrap scalars in a list or fix the shape","Convert dicts to their keys/values as a string list if that is the intended semantics","Update client and server to matching protocol versions"],"exampleFix":"# before\npayload = {\"tags\": {\"a\": 1}}\nstate = parse_session_state(payload)  # ValueError: tags must be a string or an array of strings\n# after\npayload = {\"tags\": list({\"a\": 1}.keys())}\nstate = parse_session_state(payload)","handlingStrategy":"validation","validationCode":"def ensure_str_or_str_list(payload: dict, field: str) -> None:\n    value = payload.get(field)\n    if value is None:\n        return\n    if isinstance(value, str):\n        return\n    if isinstance(value, list) and all(isinstance(x, str) for x in value):\n        return\n    raise TypeError(f\"{field!r} must be a string or list of strings, got {value!r}\")\n\nensure_str_or_str_list(payload, \"tags\")\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    logger.error(\"session state shape mismatch\", extra={\"payload\": payload, \"error\": str(e)})\n    raise ProtocolError(\"incompatible session state payload\") from e","preventionTips":["Match the documented wire schema for each field exactly","Convert dicts/objects to the expected scalar-or-list shape before sending","Keep a protocol schema test suite that validates sample payloads","Coordinate field-type changes across client and server versions"],"tags":["python","rpc","type-validation","shape-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}