Skip to content

A review lost to a GitHub 422: the response body is not logged, and a superseded run still posts #704

Description

@devops-thiago

A review on #701 was lost to a GitHub 422, and the logs cannot say why.

What happened

time event
20:53:03 ready_for_review → review starts on sha ee78bfe
20:53:20 synchronize → head moves to d4389d2 (a merge of main). ReviewDispatcher logs Coalescing review for devops-thiago/ThrillhouseBot #701 — 1 superseded request(s), latest sha: d4389d2, and the in-flight run keeps going
21:02:37 model responds after ~9.5 minutes — 100,860 tokens, 65,339 of them reasoning
21:02:39 POST /pulls/701/reviews422, ReviewPostException, user sees "review could not be completed"
21:02:40 the coalesced run for d4389d2 starts from scratch
ERROR [ReviewOrchestrator] Review failed for devops-thiago/ThrillhouseBot #701:
  ReviewPostException: GitHub review rejected for devops-thiago/ThrillhouseBot #701
Caused by: ClientWebApplicationException: Received: 'Unprocessable Entity, status code 422'
  when invoking REST Client method: 'GitHubReviewClient#createReviewOnce'

Established from the logs and the API: the request carried no inline comments
(createReviewWithFallback throws ReviewPostException only when req.comments().isEmpty(), and no
"retrying without comments" warning was logged), the round had 0 new findings and 0 unresolved
previous, so this was postNoIssuesReview. Not a force-push — ee78bfe is still a commit of the PR.
Not a draft — converted to draft 19:05, back to ready 20:53:02. Not an over-long body —
CreateReviewRequest caps it through CommentBodyLimit.

1. The 422 body is never logged

DefaultMicroprofileRestClientExceptionMapper surfaces the status and nothing else, so GitHub's
message — the one thing that would name the cause — is discarded. The failure is undiagnosable after
the fact, which is why this issue can rank causes but not state one.

Two candidates remain: a commit_id (ee78bfe) that is 9.5 minutes and one merge commit stale, or
something specific to the APPROVE event. Worth noting for the second: every bot review on this
repository is COMMENTED; none has ever landed as APPROVED
, so this plausibly was the first
APPROVE attempt.

Fix: capture the response entity on a failed review post and log it.

2. A superseded review still posts

The dispatcher knew at 20:53:20 that the run was superseded, and the run still posted 9.5 minutes
later against a head that had moved. There is no head-sha check in ReviewOrchestrator before
posting.

Re-reading the PR head before submitting, and abandoning the post when it has moved, would have
avoided this entirely: the coalesced run redid the work one second later regardless. It also stops
inline comments being resolved against a diff that changed underneath them, which is the more common
shape of this bug.

3. The no-comments path has no fallback

createReviewWithFallback retries without inline comments when a review carrying them is rejected.
When the review has no comments to drop, it rethrows, so the whole run is discarded — here ~100k
tokens and 9.5 minutes of wall clock, with an error surfaced to the user in place of a review.

Whatever the 422 turns out to be, a summary-only review that GitHub refuses should not cost the
entire generation. Falling back to an issue comment carrying the same body would preserve it.

Not reproducible on demand

#702 and #703 ran in the same window and completed normally (0 findings, COMMENT). Only #701 saw a
push land mid-review, which is what makes (2) the trigger and (1) the reason it cannot be confirmed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingjavaPull requests that update java code

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions