CI is red on a Go repo. The only real failure is the lint job, which logs:
Written to /workspace/solution.md. Here is the deliverable:
golangci-lint-action@v6 cannot install a golangci-lint v2 binaryCI is red on a Go repo. The only real failure is the lint job, which logs:
Error: invalid version string v2.12.2, golangci-lint v2 is not supported by
golangci-lint-action v6, you must update to golangci-lint-action v7
The code is fine. The linter is fine. The action major version and the pinned linter version are out of sync.
golangci/golangci-lint-action and the golangci-lint binary are two separate versioned artifacts with two separate compatibility contracts:
| Artifact | Major that understands golangci-lint v2 |
|---|---|
golangci-lint binary |
v2.x |
golangci/golangci-lint-action |
v7+ |
The workflow pinned the linter to v2.12.2 (correct, matches the local toolchain) but left the action at v6:
uses: golangci/golangci-lint-action@v6 # <-- action major: too old
with:
version: v2.12.2 # <-- linter major: v2
v6 of the action hard-codes the v1 install/validation path. When it receives version: v2.12.2 it rejects the version string instead of downloading it. Pinning the linter alone does not pin the action; upgrading the linter to v2 silently invalidated the older action.
Why this is not a billing block or a real lint failure: the job actually executed (its step list is non-empty) and the failure text is emitted by the action itself, verbatim, before any Go source is linted.
# 1. Find the failing run.
gh run list --branch "$(git rev-parse --abbrev-ref HEAD)" --limit 5
RUN_ID=<failing-run-id>
# 2. Confirm the job actually ran (steps > 0 ⇒ not a billing/spending block).
gh run view "$RUN_ID" --json jobs \
--jq '.jobs[] | {name, conclusion, stepCount: (.steps | length)}'
# 3. Get the verbatim action error.
gh run view "$RUN_ID" --log-failed | grep -i "invalid version string"
# 4. Prove the gate is innocent: run the exact pinned linter locally.
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2
golangci-lint run
# => exits 0, "0 issues."
If the local run is clean, the CI failure is 100% the action/linter version mismatch, not the source code.
Edit the lint workflow (commonly .github/workflows/lint.yml or ci.yml) and bump only the action major from v6 to v7. Leave the linter pin at v2.12.2.
- - uses: golangci/golangci-lint-action@v6
+ - uses: golangci/golangci-lint-action@v7
with:
version: v2.12.2
Full resulting step for reference:
- name: golangci-lint
uses: golangci/golangci-lint-action@v7
with:
version: v2.12.2
# optional, keep if already present:
# args: --timeout=5m
# only-new-issues: true
# working-directory: ./
Apply, commit, and push:
git add .github/workflows/*.yml
git commit -m "ci: bump golangci-lint-action to v7 for golangci-lint v2"
git push
Do not also bump the linter to latest, and do not downgrade the linter to v1. The local toolchain is v2.12.2; CI must stay on the same linter major so results are reproducible. Only the action major changes.
# 1. YAML is valid and the pin is correct.
grep -n "golangci-lint-action@" .github/workflows/*.yml
# => uses: golangci/golangci-lint-action@v7
grep -n "version:" .github/workflows/*.yml
# => version: v2.12.2
# 2. Local gate still green (same linter CI will use).
golangci-lint run
# => 0 issues.
# 3. Watch the new CI run end-to-end.
gh run list --branch "$(git rev-parse --abbrev-ref HEAD)" --limit 1
RUN_ID=<new-run-id>
gh run watch "$RUN_ID" --exit-status
# => lint job conclusion: success
# 4. Confirm the action no longer errors.
gh run view "$RUN_ID" --log \
| grep -iE "invalid version string|not supported by golangci-lint-action" \
&& echo "STILL BROKEN" || echo "action/linter mismatch resolved"
Success criteria:
gh run watch --exit-status exits 0.success.invalid version string / not supported by golangci-lint-action text in logs.golangci-lint run locally still reports 0 issues.version: fixes the binary; it does not make an old action understand it.golangci-lint-action@v6 → golangci-lint v1; @v7+ → v2.v2.12.2 here) so a red CI can never be blamed on linter drift.# Evidence - Problem class: go-github-actions-lint-action-major-mismatch - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-23T21:44:46.843Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "CI RED on a Go repo whose only real failure is the lint step: golangci-lint-action@v6 cannot install a golangci-lint v2 binary - it errors \"invalid version string v2.12.2, golangci-lint v2 is not supported by golangci-lint-action v6, you must update to golangci-lint-action v7\". Diagnostic that separates this from a real lint failure: read the failed JOB's steps (gh run view <id> --json jobs) - if steps>0 the job really ran, so it is not a billing block, and gh run view --log-failed names the action error verbatim. Confirm the gate itself is innocent by running the linter locally (golangci-lint run -> 0 issues), then bump ONLY the action major to v7 while keeping the linter version pinned (v2.12.2) so CI matches the local toolchain. Lesson: the action major and the linter major are separate contracts; pinning the linter alone does not pin the action.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-github-actions-lint-action-major-mismatch", "provider": "openrouter", "solved_at": "2026-09-23T21:44:46.846Z", "version": ""}