Debugging lint-staged failures Jump to heading

When lint-staged fails, the commit stops and the terminal fills with a task list, some ticks, a cross and a block of tool output. Sometimes the cause is obvious — a lint error in your code. Often it is not: a task reports “command not found” though the tool works in your shell, nothing runs at all for a file you clearly staged, the commit is blocked as “empty”, or lint-staged reports a Git error about stashes. Each has a handful of causes, and lint-staged gives you the tools to tell them apart. This page goes through reading the output, reproducing a task by hand, and the fixes for each common failure, within lint-staged formatting automation.

When to use this approach Jump to heading

  • A commit fails in the lint-staged hook and the reason is not a lint error in your change.
  • lint-staged works for some people on the team and not others.
  • Tasks are not running for files you expect them to cover.
  • The hook was working and broke after a dependency or tool upgrade.

Step 1 — Read which task failed and why Jump to heading

lint-staged prints each glob, its tasks and their status, then the output of the failed task. Find the cross, then read the tool output beneath it.

✔ Preparing lint-staged...
❯ Running tasks for staged files...
  ❯ .lintstagedrc.json — 2 files
    ❯ *.{ts,tsx} — 2 files
      ✖ eslint --fix [FAILED]
      ◼ prettier --write
↓ Skipped because of errors from tasks.
✔ Reverting to original state because of errors...

✖ eslint --fix:
/repo/src/billing/totals.ts
  14:7  error  'discount' is assigned a value but never used  no-unused-vars

“Reverting to original state” means your files are exactly as they were before the commit attempt. Nothing was lost.

Step 2 — Reproduce the task by hand Jump to heading

Run the failed command yourself on the same files, from the same directory lint-staged used. If it fails the same way, the problem is the tool or your code; if it succeeds, the problem is the hook environment.

npx lint-staged --debug 2>&1 | grep -E 'cwd|Running|Command'   # exact command and directory
cd packages/web && npx eslint --fix src/billing/totals.ts       # same command, same place
Where is the problem?If the command fails the same way by hand, the cause is the code or the tool configuration. If it succeeds by hand but fails in the hook, the cause is the hook's environment — PATH, working directory or Node version. If the task never ran, the glob or configuration discovery is the cause.Run the failing task by hand — what happens?fails the sameCode or tool configfix the errorworks by handHook environmentPATH, cwd, Node versiontask never ranGlob or configcheck matchingthis one test separates most problems in under a minute

Step 3 — Fix “command not found” and environment differences Jump to heading

When a task works in your shell but fails in the hook, the hook is running with a different environment — common in GUI clients and editors, which do not load shell profiles, and with Node version managers.

# What PATH and Node does the hook see?
printf 'echo "PATH=$PATH"; command -v node; node --version\n' > .husky/pre-commit.debug
sh .husky/pre-commit.debug

Run tools through the package manager (npx, pnpm exec) rather than by bare name, so they resolve from node_modules. For Node version managers, Husky can source an init script before every hook; the details are in Husky hooks in GUI clients and IDEs.

Step 4 — Fix tasks that never run Jump to heading

If a staged file is not processed, either no glob matches it or the wrong configuration file was found. Two details cause most of these: globs without a slash match against the file’s base name, while globs with a slash match the path relative to the config file; and in a monorepo, the nearest config wins.

# Which config and which glob does lint-staged pick for each staged file?
npx lint-staged --debug 2>&1 | grep -E 'Found config|Matched|no tasks'
// Matches any .ts file at any depth (no slash: base name)
{ "*.ts": "eslint --fix" }
// Matches only .ts files directly in src/ (has a slash: relative path)
{ "src/*.ts": "eslint --fix" }
// Matches .ts files anywhere under src/
{ "src/**/*.ts": "eslint --fix" }
How lint-staged globs matchA glob without a slash matches each file's base name, so it applies at any depth. A glob with a slash matches the path relative to the configuration file, so src/*.ts covers only direct children. Double-star patterns match any depth below a directory.GlobMatches src/a/b.ts?*.tsbase nameyessrc/*.tsrelative pathnosrc/**/*.tsrelative pathyesa slash changes the matching mode — the most common cause of 'nothing ran' The four failure familiesMost lint-staged failures fall into four families: a real lint or format error in the code, a hook environment that cannot find or run the tool, configuration that never matched the file, and Git-level errors while hiding or restoring unstaged changes. Each has its own first diagnostic step.Codetool reports errorfix the codeEnvironmentnot found by hookPATH, NodeConfigurationtask never ranglob, config fileGitstash / empty commitrecover backupname the family first; the fix follows from it

Step 5 — Fix empty-commit and stash errors Jump to heading

Two Git-level errors look alarming but have plain causes.

✖ Prevented an empty git commit!

The tasks reverted every staged change — usually a formatter undoing a formatting-only change you staged, because your editor and the project’s formatter disagree. Align the editor’s formatter settings with the project’s configuration, or commit with --allow-empty if an empty commit is really intended.

✖ lint-staged failed due to a git error.
  Any lost modifications can be restored from a git stash

Something went wrong while hiding or restoring unstaged changes. Your work is in the backup stash; recover it as described in handling partially staged files.

git stash list | grep 'lint-staged automatic backup'
git stash apply --index 'stash@{0}'

⚠️ SAFETY WARNING: If lint-staged fails repeatedly with stash errors, do not “fix” it by deleting .git/index.lock or clearing stashes blindly. A stale index.lock usually means another Git process is running — an editor, a file watcher — so close it first. Clear stashes only after confirming your work is restored.

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Is it safe to commit with --no-verify to get past a broken hook? Jump to heading

As a one-off to save work, yes — CI still checks the code. Fix the hook promptly, though; a hook people routinely bypass protects nothing.

Why does lint-staged say “No staged files match any configured task”? Jump to heading

Either nothing you staged matches a glob — check with --debug — or the configuration was not found because it is in a package directory above none of your files. It is informational, not an error.

Why did the hook pass locally but CI failed on the same files? Jump to heading

Different tool versions or configurations, or CI checking the whole file where lint-staged checked only staged content. Pin tool versions, and run the same configuration in CI with lint-staged --diff.