# reviewdog: fail to post review: pull request is closed or merged - how to fix

## TL;DR
The reviewdog CI job ran against a pull request that was already merged or closed, so GitHub rejects reviewdog's review submission with a 422 and reviewdog exits non-zero, failing the job. First confirm the PR state with `gh pr view`; if it is merged or closed, the findings are moot and rerunning will fail the same way. Then stop the job from running on closed PRs: restrict the workflow's `pull_request` activity types to open-PR events, or guard the reviewdog job with a PR-state condition. If you need findings posted even after merge or close, switch the reporter to `github-check`: the Checks API posts to the commit SHA and does not need an open PR.

## The failure signature

```text
reviewdog: failed to post a review comment: POST https://api.github.com/repos/[owner]/[repo]/pulls/[number]/reviews: 422 Validation Failed [{Resource:PullRequestReview Field:base Message:Pull request reviews may only be submitted on open pull requests}]
```

You will usually see this in the CI log right after the linter output, and the reviewdog step (and the whole job) is marked failed. The linter itself ran fine; only the final review post failed.

## The fix

### Step 1 - Confirm the PR is actually closed or merged

```bash
gh pr view [number] --json state,mergedAt
```

Expected output: something like `{"state": "MERGED", "mergedAt": "..."}` or `{"state": "CLOSED", ...}`. If it says `OPEN`, this is a different problem (check token scopes and the other reviewdog skills instead).

### Step 2 - Check whether the findings already landed on an earlier run

Open the PR page and look for the reviewdog review posted by an earlier CI run. The diagnostics usually landed before the merge; a failed re-post adds nothing.

Expected output: either the review is already visible on the PR (nothing to do), or the PR merged so fast no run ever posted (continue to step 3).

### Step 3 - Stop the workflow from triggering on closed PRs

The most common cause: `on: [pull_request]` fires on every activity type, including `closed`. Restrict it to open-PR activity types:

```yaml
on:
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]
```

Expected output: future merges and closes no longer start a reviewdog run, so this failure cannot recur.

### Step 4 - Guard the reviewdog job against a mid-run merge

Even with restricted types, a PR can merge while the job is queued or running (auto-merge, fast-moving PRs). Add a state guard on the reviewdog job:

```yaml
jobs:
  lint:
    if: github.event.pull_request.state == 'open'
    runs-on: ubuntu-latest
    steps:
      - run: reviewdog -f=rdjson -reporter=github-pr-review ...
```

Note: this condition only works on `pull_request` events; `push` and `workflow_dispatch` events have no `pull_request` payload.

Expected output: the job is skipped instead of failing red when the PR merged before the review posted.

### Step 5 - Switch reporter if you need findings on closed or merged PRs

If your workflow must run after merge or close (for example a post-merge check on `pull_request_target`, or findings you want attached to the merge commit), change `-reporter=github-pr-review` to `-reporter=github-check`. The Checks API posts to the commit SHA, which works fine on closed and merged PRs.

Expected output: findings appear as check-run annotations instead of inline review comments, and the job stays green after merge.

## Use this when

- reviewdog logs `failed to post a review comment` in a GitHub Actions job
- The 422 message says pull request reviews may only be submitted on open pull requests
- The job failed after the PR was merged or closed, or someone hit Re-run jobs on a merged PR's check run
- Your reviewdog workflow triggers on bare `on: [pull_request]` (which includes the `closed` activity type)

## Not for

- `reviewdog: annotation failed to post: resource not accessible by integration` - that is a token permission problem, not a closed PR (see the reviewdog permissions skill)
- `reviewdog: error: fail to parse diff: unexpected hunk header` - that is a diff-format problem (see the reviewdog diff skill)
- Reviews submitted through Octokit or the GitHub API directly by your own agent code - the Octokit "Pull request reviews may only be submitted on open pull requests" skill covers that path
- reviewdog never posting at all with no error - check the token (`REVIEWDOG_GITHUB_API_TOKEN`) and reporter configuration first

## Tool compatibility

- reviewdog: all recent versions (verified against reviewdog source, master 2026-10). The behavior is in the `github-pr-review` reporter: it posts the review in `Flush` at the very end of the run via the GitHub CreateReview API, and treats a failed post as a fatal error.
- GitHub REST API, pull request reviews endpoint; GitHub Actions. Only the `github-pr-review` reporter needs an open PR; `github-check` and `github-pr-check` post to the commit SHA instead.

## Variant phrasings

### reviewdog failed to post a review comment: 422 Validation Failed on merged PR

Same failure, one layer down. reviewdog does not retry or fall back for a closed-PR 422; it logs the line above and returns the error, so the step fails. The workflow-level guards in steps 3 and 4 are the fix.

### reviewdog CI job fails after PR auto-merges

The race in its purest form: the PR auto-merged while the CI job was queued, and reviewdog's final review POST hit a merged PR. Step 4's state guard turns this from a red failure into a clean skip.

### reviewdog run triggered by pull_request closed event

A bare `on: [pull_request]` includes the `closed` activity type, so merging or closing a PR starts a reviewdog run whose review post is guaranteed to fail. Restrict the activity types (step 3) unless you also switch to the `github-check` reporter.

## Why it happens

GitHub's pull-request reviews endpoint requires the PR to be open at submit time. reviewdog collects findings during the run and submits them as one review in `Flush`, at the very end - so a merge or close that happens at any point during the run invalidates the final POST. The failure is timing, not permissions: the same job would have passed minutes earlier. reviewdog treats the failed post as fatal (there is no fallback for a non-permission 422, unlike the log fallback it uses for 403/404), which is why one closed PR turns a clean lint run into a red check.

## Edge cases

- Auto-merge and fast merges: the guard in step 4 is the only reliable protection, because no trigger filter can prevent a merge that happens between queue time and post time.
- Merge queue: GitHub merge queue merges produce the same failure on reruns; the state guard covers it.
- Re-run jobs button: rerunning a merged PR's check run replays the whole job, including reviewdog's doomed review post. The guard turns it into a skip.
- `github-check` tradeoffs: you lose inline review comments on the diff; annotations land on the check run instead, limited to 50 annotations per request and a 65535-character summary. Good for post-merge visibility, worse for line-level review discussion.
- If you genuinely need a review comment on the closed PR anyway: reviews cannot be posted to closed PRs by any tool. Convert the findings into regular PR conversation comments (which still work on closed and merged PRs) or open a follow-up PR.
