reviewdog error: no filter matched for changed files
Fixes reviewdog's no filter matched for changed files error by correcting the diff filter patterns so they match the actual changed paths. Use when reviewdog exits without posting findings even though the PR changed files. Not for reviewdog diff-parse errors, annotation permission failures, or linters that genuinely found nothing.
TL;DR
Reviewdog skips every file when its filter patterns match nothing in the diff. Usually the include or exclude globs use the wrong path prefix (repo-root vs subdirectory) or the diff paths changed shape. Print the changed file list, test the filter against it, and fix the pattern. An empty filter match is a config bug, not a clean bill of health.
The query
reviewdog error: no filter matched for changed filesUse this when
- Reviewdog exits with "no filter matched for changed files" on a PR that changed files.
- Reviewdog posts zero findings on PRs that clearly have lint issues.
- The filter config was recently added or edited.
Not for
reviewdog: error: fail to parse diff: unexpected hunk header(that is a diff-format problem).annotation failed to post: resource not accessible by integration(that is a permissions problem).- A linter that genuinely found nothing (then reviewdog correctly posts nothing).
Steps
Step 1: List the changed files reviewdog should see
git diff --name-only origin/[base]...HEADExpected output: the actual changed paths, relative to the repo root. Keep this list open. Every filter pattern must match at least one of these paths, or the filter is wrong by definition.
Step 2: Inspect the filter flags in the workflow
grep -n -A 10 "reviewdog" .github/workflows/[workflow].ymlExpected output: the reviewdog invocation with its -filter-mode and any include or exclude patterns. Common culprit: patterns anchored at a subdirectory while the diff paths are repo-root relative, or vice versa.
Step 3: Test the glob semantics
echo "changed: src/api/handler.go"
echo "pattern **/*.go matches from repo root; *.go does not match nested paths"Expected output: the realization that reviewdog uses doublestar globs. *.go matches only top-level files. **/*.go matches at any depth. Most "no filter matched" cases are a missing **/ prefix.
Step 4: Verify reviewdog receives the full diff
git diff origin/[base]...HEAD | head -20
git diff origin/[base]...HEAD | wc -lExpected output: a non-empty diff piped to reviewdog. If the workflow passes a truncated or empty diff (wrong base ref, shallow clone), the filter has nothing to match regardless of the pattern.
Step 5: Fix the pattern and re-run
- uses: reviewdog/action-golangci-lint@v2
with:
filter_mode: diff_context
fail_level: errorExpected output: with the corrected pattern and filter_mode appropriate to the use case (diff_context for new issues, added for added lines only), reviewdog posts findings on the changed files instead of the no-match message.
Step 6: Add a guard so silent zero-findings fail loudly
changed=$(git diff --name-only origin/[base]...HEAD | wc -l)
[ "$changed" -gt 0 ] && echo "reviewdog must report or explain"Expected output: a CI assertion that fails the step when the PR changed files but reviewdog reported nothing and gave no reason. The next filter regression becomes a red build, not a silent pass.
Variant phrasings
reviewdog error: all golangci-lint checks skipped, no files matched
Same filter mismatch, one level down: the linter ran but its file selection matched nothing. Fix the linter's path config the same way.
reviewdog error: diff is empty when agent tried to review a draft PR
The diff input was empty, so every filter trivially matched nothing. Fix the diff generation (fetch the base ref, unshallow the clone) before touching filters.
reviewdog posts nothing on PRs but works on main
The base ref differs between the two contexts. Step 4's diff check catches it.
Why it happens
Filter mode defaults to matching changed lines, and the include/exclude globs are evaluated against diff paths that are relative to the repo root. When the pattern was written against a different root (a subdirectory checkout, a monorepo package path, a docs example), nothing matches, reviewdog finds no files to check, and it reports the no-match message instead of failing. The agent reads "no findings" as "clean PR" and moves on, which is exactly backwards.
Edge cases
- Renamed files appear in the diff with old-to-new path syntax. A filter matching the old path misses the new one. Anchor on the new path.
filter_mode: nofilterdisables filtering entirely and checks the whole codebase. Useful for debugging, but do not ship it as the fix. It will drown the PR in pre-existing findings.- Shallow clones (
fetch-depth: 1) cannot produce the merge-base diff. The diff is empty, the filter matches nothing, and the error message blames the filter. - On monorepos, run one reviewdog invocation per package with package-rooted patterns rather than one invocation with a pattern that tries to cover everything.
- If the linter itself crashed, reviewdog may report no findings instead of the crash. Check the linter's exit code independently.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstHVIztv0ArgkNHEPeppDfg
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.