can1357/oh-my-pi · error · HTTPException
comments[{idx}] must be an object
Error message
comments[{idx}] must be an object What it means
Each element of the `comments` array in a PR review must be an object with path, line, and body. `_require_review_comments` raises this HTTP 400 when an array element is not a JSON object (e.g. a bare string or number).
Source
Thrown at python/robomp/src/proxy/server.py:188
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"):
raise HTTPException(400, f"comments[{idx}].start_side must be RIGHT or LEFT")
comment["start_side"] = start_side_str
comments.append(comment)
return commentsView on GitHub (pinned to 9690622007)
Solutions
- Send each comment as an object with required keys path (string), line (int), and body (string).
- Build comments programmatically: comments.append({"path": p, "line": l, "body": b}) rather than appending raw strings.
- Validate the payload shape client-side before the request (see defense type guard).
- Check for accidental flattening — e.g. list(dict) yields keys only, not objects.
Example fix
// before
"comments": ["a.py looks wrong"]
// after
"comments": [{"path": "a.py", "line": 1, "body": "a.py looks wrong"}] Defensive patterns
Strategy: validation
Validate before calling
def check_comments(comments):
for c in comments:
if not isinstance(c, dict):
raise ValueError("each comment must be an object")
for k in ("path", "body"):
if not isinstance(c.get(k), str):
raise ValueError(f"comment.{k} must be a string")
if not isinstance(c.get("line"), int) or isinstance(c["line"], bool):
raise ValueError("comment.line must be an int") Type guard
def is_comment(c: object) -> TypeGuard[dict]:
return (
isinstance(c, dict)
and isinstance(c.get("path"), str)
and isinstance(c.get("line"), int)
and not isinstance(c.get("line"), bool)
and isinstance(c.get("body"), str)
) 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 "must be an object" in e.response.text:
idx = int(e.response.text.split("comments[")[1].split("]")[0])
raise ValueError(f"comments[{idx}] is not an object: {payload['comments'][idx]!r}") from e
raise Prevention
- Build each comment as a dict with path/line/body keys, never raw strings
- Add a local payload validator mirroring the server's rules
- Don't pass list(dict) or other accidental key-flattening results as comments
- Unit-test payload builders so a shape regression fails before the HTTP call
When it happens
Trigger: Posting submit_pr_review with comments like ["leave a note"] or ["path a.py", 3] instead of objects such as [{"path": "a.py", "line": 3, "body": "note"}].
Common situations: CLI tools accepting free-text review notes and passing them through unstructured; clients submitting an array of file paths expecting the server to attach a default message; template mistakes dropping the object braces.
Related errors
- missing/invalid 'comments'
- missing/invalid 'slot_uid'
- invalid '{field}': must be array of strings
- comments[{idx}].side must be RIGHT or LEFT
- comments[{idx}].start_side must be RIGHT or LEFT
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/380b42c8ea875f46.
Report an issue: GitHub.