{"record":{"id":"21774b467bbc1e7a","repo":"can1357/oh-my-pi","slug":"invalid-field-must-be-array-of-strings","errorCode":null,"errorMessage":"invalid '{field}': must be array of strings","messagePattern":"invalid '(.+?)': must be array of strings","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"python/robomp/src/proxy/server.py","lineNumber":176,"sourceCode":"    tag = _require_str(value, \"tag\")\n    if not _RELEASE_TAG_RE.fullmatch(tag):\n        raise HTTPException(400, \"invalid tag\")\n    return tag\n\n\ndef _optional_slot_uid(value: Any) -> int | None:\n    if value is None:\n        return None\n    if not isinstance(value, int) or isinstance(value, bool) or not (0 < value < 65536):\n        raise HTTPException(400, \"missing/invalid 'slot_uid'\")\n    return value\n\n\ndef _optional_str_list(value: Any, field: str) -> list[str] | None:\n    if value is None:\n        return None\n    if not isinstance(value, list) or not all(isinstance(v, str) for v in value):\n        raise HTTPException(400, f\"invalid '{field}': must be array of strings\")\n    return list(value)\n\n\ndef _require_review_comments(value: Any) -> list[dict[str, Any]]:\n    if value is None:\n        return []\n    if not isinstance(value, list):\n        raise HTTPException(400, \"missing/invalid 'comments'\")\n    comments: list[dict[str, Any]] = []\n    for idx, item in enumerate(value):\n        if not isinstance(item, dict):\n            raise HTTPException(400, f\"comments[{idx}] must be an object\")\n        path = _require_str(item.get(\"path\"), f\"comments[{idx}].path\")\n        line = _require_int(item.get(\"line\"), f\"comments[{idx}].line\")\n        body = _require_str(item.get(\"body\"), f\"comments[{idx}].body\")\n        side = str(item.get(\"side\") or \"RIGHT\")\n        if side not in (\"RIGHT\", \"LEFT\"):\n            raise HTTPException(400, f\"comments[{idx}].side must be RIGHT or LEFT\")","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/proxy/server.py#L158-L194","documentation":"`_optional_str_list` validates optional list-valued fields (reviewers, labels, assignees). The value must be absent/null or a JSON array whose every element is a string; anything else raises this HTTP 400 naming the offending field.","triggerScenarios":"Calling request_reviewers, add_issue_labels, or add_assignees with a field that is a single string instead of an array (e.g. \"alice\" instead of [\"alice\"]), an array containing numbers/nulls, or a non-list object.","commonSituations":"Clients passing a comma-separated string from CLI input; scripts building payloads with one element and forgetting to wrap it in a list; arrays containing None entries after filtering failures.","solutions":["Always wrap single values in an array: [\"alice\"] not \"alice\".","Ensure every element is a string — stringify numbers (e.g. issue numbers) before adding to label/assignee lists.","Filter nulls/undefined from the array client-side before sending.","If you want no change, omit the field entirely rather than sending an empty non-list value."],"exampleFix":"// before\n{\"reviewers\": \"alice\"}\n// after\n{\"reviewers\": [\"alice\"]}","handlingStrategy":"validation","validationCode":"def as_str_list(v):\n    if v is None:\n        return None\n    if isinstance(v, str):\n        v = [v]\n    if not isinstance(v, list) or not all(isinstance(x, str) for x in v):\n        raise ValueError(f\"{field} must be an array of strings\")\n    return [x for x in v if x is not None]","typeGuard":"def is_str_list(v: object) -> TypeGuard[list[str]]:\n    return isinstance(v, list) and all(isinstance(x, str) for x in v)","tryCatchPattern":"try:\n    resp = http.post(f\"{base}/issues/{n}/labels\", json={\"labels\": labels})\n    resp.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 400 and \"must be array of strings\" in e.response.text:\n        raise ValueError(\"labels/reviewers/assignees must be list[str]\") from e\n    raise","preventionTips":["Always wrap single values in a list before sending list-typed fields","Coerce numeric/None entries to strings or filter them out first","Use typed client helpers (dataclasses/pydantic) so the shape is enforced at build time","Log the serialized payload when debugging 400s to spot type drift"],"tags":["http-400","input-validation","fastapi","type-error"],"backgroundTag":"request-parameter-validation","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}