can1357/oh-my-pi · error · HTTPException

missing/invalid 'comments'

Error message

missing/invalid 'comments'

What it means

`_require_review_comments` validates the `comments` array of a PR review submission. `comments` must be a JSON array (null is tolerated and becomes []); a non-array value raises this HTTP 400. Individual element validation produces the more specific comments[idx] errors instead.

Source

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

        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")
        comment: dict[str, Any] = {"path": path, "line": line, "side": side, "body": body}
        start_line = item.get("start_line")
        if start_line is not None:
            comment["start_line"] = _require_int(start_line, f"comments[{idx}].start_line")
        start_side = item.get("start_side")
        if start_side is not None:
            start_side_str = _require_str(start_side, f"comments[{idx}].start_side")
            if start_side_str not in ("RIGHT", "LEFT"):

View on GitHub (pinned to 9690622007)

Solutions

  1. Wrap the comment payload in an array: [{"path": ..., "line": ..., "body": ...}].
  2. If the value is a JSON string, parse it client-side before posting.
  3. Omit `comments` entirely (or send null) for review verdicts without comments — null is accepted.
  4. Inspect the request body with a debugger/logging middleware to confirm the actual encoded type.

Example fix

// before
{"event": "COMMENT", "comments": {"path": "a.py", "line": 3, "body": "fix"}}
// after
{"event": "COMMENT", "comments": [{"path": "a.py", "line": 3, "body": "fix"}]}
Defensive patterns

Strategy: type-guard

Validate before calling

comments = raw if isinstance(raw, list) else ([raw] if isinstance(raw, dict) else None)
if comments is None:
    raise ValueError("comments must be a list of comment objects")

Type guard

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

Try / catch

try:
    resp = http.post(f"{base}/pr/{n}/review", json=payload)
    resp.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 400 and "'comments'" in e.response.text:
        raise ValueError("comments must be a JSON array, not " + type(payload['comments']).__name__) from e
    raise

Prevention

When it happens

Trigger: Calling submit_pr_review with `comments` as an object, a string, or a number — e.g. sending a single comment object directly instead of wrapping it in a list, or sending comments as a JSON-encoded string.

Common situations: Clients double-encoding comments (JSON string inside JSON); SDK misuse where a single-comment convenience path forgets the array wrapper; copying a comments object from a GET response and posting it back unmodified.

Related errors


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