can1357/oh-my-pi · error · HTTPException

invalid '{field}': must be array of strings

Error message

invalid '{field}': must be array of strings

What it means

`_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.

Source

Thrown at python/robomp/src/proxy/server.py:176

    tag = _require_str(value, "tag")
    if not _RELEASE_TAG_RE.fullmatch(tag):
        raise HTTPException(400, "invalid tag")
    return tag


def _optional_slot_uid(value: Any) -> int | None:
    if value is None:
        return None
    if not isinstance(value, int) or isinstance(value, bool) or not (0 < value < 65536):
        raise HTTPException(400, "missing/invalid 'slot_uid'")
    return value


def _optional_str_list(value: Any, field: str) -> list[str] | None:
    if value is None:
        return None
    if not isinstance(value, list) or not all(isinstance(v, str) for v in value):
        raise HTTPException(400, f"invalid '{field}': must be array of strings")
    return list(value)


def _require_review_comments(value: Any) -> list[dict[str, Any]]:
    if value is None:
        return []
    if not isinstance(value, list):
        raise HTTPException(400, "missing/invalid 'comments'")
    comments: list[dict[str, Any]] = []
    for idx, item in enumerate(value):
        if not isinstance(item, dict):
            raise HTTPException(400, f"comments[{idx}] must be an object")
        path = _require_str(item.get("path"), f"comments[{idx}].path")
        line = _require_int(item.get("line"), f"comments[{idx}].line")
        body = _require_str(item.get("body"), f"comments[{idx}].body")
        side = str(item.get("side") or "RIGHT")
        if side not in ("RIGHT", "LEFT"):
            raise HTTPException(400, f"comments[{idx}].side must be RIGHT or LEFT")

View on GitHub (pinned to 9690622007)

Solutions

  1. Always wrap single values in an array: ["alice"] not "alice".
  2. Ensure every element is a string — stringify numbers (e.g. issue numbers) before adding to label/assignee lists.
  3. Filter nulls/undefined from the array client-side before sending.
  4. If you want no change, omit the field entirely rather than sending an empty non-list value.

Example fix

// before
{"reviewers": "alice"}
// after
{"reviewers": ["alice"]}
Defensive patterns

Strategy: validation

Validate before calling

def as_str_list(v):
    if v is None:
        return None
    if isinstance(v, str):
        v = [v]
    if not isinstance(v, list) or not all(isinstance(x, str) for x in v):
        raise ValueError(f"{field} must be an array of strings")
    return [x for x in v if x is not None]

Type guard

def is_str_list(v: object) -> TypeGuard[list[str]]:
    return isinstance(v, list) and all(isinstance(x, str) for x in v)

Try / catch

try:
    resp = http.post(f"{base}/issues/{n}/labels", json={"labels": labels})
    resp.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 400 and "must be array of strings" in e.response.text:
        raise ValueError("labels/reviewers/assignees must be list[str]") from e
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/21774b467bbc1e7a. Report an issue: GitHub.