## TL;DR
The linter found real issues and exited 1, which fails the gate. That is the gate working. Run the same command locally, fix the findings (or triage them deliberately), and consider gating only on issues introduced by the PR so pre-existing debt stops blocking new work.

## The error
```text
golangci-lint: level=error msg=exit status 1: review gate failed on the PR
```

## Steps

### 1. Reproduce the exact CI command locally
Run what CI runs, from the repo root:
```bash
golangci-lint run ./...
```
Expected output: the same list of findings CI reported.

### 2. Read the findings, not just the exit code
The `level=error msg=exit status 1` line is the wrapper. Scroll up for the per-issue lines naming files, lines, and linters.
Expected output: a concrete list of issues to fix.

### 3. Fix the findings
Fix the code the linters flag. For deliberate exceptions, use a targeted nolint comment naming the specific linter, not a blanket suppression.
Expected output: the finding count drops.

### 4. Gate only on new issues
To stop old debt from blocking PRs, limit the gate to issues on changed lines:
```bash
golangci-lint run --new-from-rev=origin/main ./...
```
Expected output: only findings introduced by the PR fail the gate.

### 5. Verify a clean exit
Re-run the CI command locally until it exits 0, then push.
Expected output: the gate passes on the PR.

## When to use
- The exact exit-status-1 gate failure
- A new linter or enabled check starts failing PRs
- Legacy code failing a newly strict config

## When not to use
- golangci-lint crashing (out of memory, panics)
- Config file errors (the run never starts)
- Version differences between local and CI

## Tool compatibility
- golangci-lint v1.55 and later (v2 changed some flag names)
- Go 1.20 and later
- GitHub Actions and other CI runners

## Variant phrasings
### golangci-lint exit status 1 on PR
Same fix. Read the findings above the summary line.

### golangci-lint failing only in CI
Check the version and the config path. CI may resolve a different config file than your local run.

### how to ignore old golangci-lint issues
The new-from-rev flag in step 4 is the standard approach.

## Why it happens
golangci-lint exits nonzero when any enabled linter reports an issue. CI treats nonzero as failure. The terse log line hides the actual findings, which is why the first step is always reproducing the full output locally.

## Edge cases
- new-from-rev needs the base ref fetched. Shallow checkouts break it the same way they break reviewdog diffs.
- A blanket nolint without a linter name hides future issues too. Name the linter.
- Linter version skew: pin the golangci-lint version in CI so local and CI agree.
- Very large repos: the full run can be slow. The new-issues mode is faster as a side benefit.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_4Z0YEv7peGRWnSbV4ln5jw
