KhueApps
Home/DevOps/Fixing Git 'pre-commit hook failed' and unblocking commits

Fixing Git 'pre-commit hook failed' and unblocking commits

Last updated: October 07, 2025

Why commits get blocked

A Git pre-commit hook runs before a commit is created. If any hook exits with a non-zero status, Git aborts the commit and shows “pre-commit hook failed.” This is intended to prevent bad code, formatting drift, or secrets from entering the repo.

This guide shows how to diagnose failures quickly, fix the underlying issues, and, when necessary, bypass safely.

Quickstart: unblock a failing commit safely

  • Read the last lines of the hook output to identify the failing tool or check.
  • Run the failing hook or command locally as the same user and environment.
  • Apply the suggested fixes (often formatting or linting), re-stage changes, and commit again.
  • If you use the pre-commit framework (Python tool), run:
    • pre-commit install
    • pre-commit run --all-files
    • pre-commit autoupdate (if versions are stale)
  • As a last resort only, bypass temporarily with:
    • git commit --no-verify

Minimal working example (using the pre-commit framework)

This example installs two basic checks that frequently cause failures: trailing whitespace and missing EOF newline.

  1. Create the config at the repo root:
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
  1. Install and run:
# install the framework (one-time per machine)
pipx install pre-commit || pip install --user pre-commit

# register git hooks for this repo
pre-commit install

# run on all files to warm caches and fix existing issues
pre-commit run --all-files

# make a change, stage, and commit; hooks will run automatically
git add -A && git commit -m "test hooks"

If the hooks modify files (e.g., fix whitespace), you must re-stage and commit again:

git add -A && git commit -m "apply pre-commit fixes"

Diagnose and fix: step-by-step

  1. Capture the exact error
  • Re-run your commit to read the output:
    • git commit -m "msg"
  • Note the failing hook name and the command it ran.
  1. Reproduce the failure manually
  • If using the pre-commit framework:
    • pre-commit run # runs on staged files
    • pre-commit run --all-files
    • pre-commit run <hook-id> --all-files
  • If using a custom shell hook at .git/hooks/pre-commit:
    • bash -x .git/hooks/pre-commit # run with tracing
  1. Fix file issues, then re-stage
  • Many failures are from formatters/linters changing files.
  • Apply the suggested fixes or auto-fix:
    • pre-commit run --all-files
    • or run the specific tool (e.g., black, eslint, gofmt), then:
    • git add -A
  1. Verify hook executability and line endings (custom hooks)
  • Make the hook executable:
    • chmod +x .git/hooks/pre-commit
  • Ensure a valid shebang on the first line (e.g., #!/usr/bin/env bash or python3).
  • Fix CRLF issues on Windows that break shebang execution:
    • git config core.autocrlf input
    • or convert: dos2unix .git/hooks/pre-commit
  1. Ensure the hook is installed where Git expects
  • Check custom hooks path:
    • git config --get core.hooksPath # empty means .git/hooks
  • If using pre-commit framework:
    • pre-commit install # regenerates the .git/hooks/pre-commit shim
  1. Install missing runtimes and dependencies
  • Errors like “command not found” indicate missing tools.
  • Make sure Node, Python, Go, or other runtimes are on PATH inside non-interactive shells.
  • For pre-commit-managed hooks, environments are auto-created; if corrupted:
    • pre-commit clean && pre-commit run --all-files
  1. Update stale hooks and pin versions
  • For pre-commit:
    • pre-commit autoupdate
  • Commit the updated .pre-commit-config.yaml and re-run.
  1. When to bypass
  • Use only for emergencies (e.g., broken dev environment, CI covers the check):
    • git commit --no-verify
  • Follow up by fixing the hook and making a proper commit.

Common errors and how to fix them

SymptomLikely causeFix
pre-commit: command not foundFramework not installedpipx install pre-commit or pip install --user pre-commit, then pre-commit install
fatal: cannot run .git/hooks/pre-commit: No such file or directoryWrong hooks path or missing scriptCheck git config core.hooksPath; run pre-commit install or restore .git/hooks/pre-commit
Permission deniedHook not executablechmod +x .git/hooks/pre-commit
/usr/bin/env: bash
: No such file or directoryCRLF line endingsConvert to LF (dos2unix) and set core.autocrlf appropriately
Hook failed: files were modified by this hookAuto-fixer changed filesgit add -A then commit again; or run pre-commit run --all-files
Tool exits with non-zero (linter errors)Code issue or config mismatchFix code or align tool config; re-run the hook
Works locally, fails in CIDifferent PATH/runtime, missing depsInstall runtimes in CI; cache pre-commit; run pre-commit run --all-files in CI

Pitfalls to avoid

  • Assuming hooks run on unstaged content; most run on staged snapshots. Re-stage after fixes.
  • Relying on interactive shells. Hooks run non-interactively, so pyenv/nvm/asdf shims may not load; set PATH explicitly or use absolute paths.
  • Having multiple hook systems (e.g., Husky and pre-commit) both bound to pre-commit; they can conflict.
  • Ignoring hook versions; unpinned or stale versions cause drift across machines.
  • Running heavyweight checks in pre-commit that scan the whole repo; prefer pre-push or CI for slow tasks.

Performance notes

  • Run only on changed files. Pre-commit does this by default; avoid --all-files in normal workflow.
  • Limit scope using include/exclude patterns in .pre-commit-config.yaml to skip vendor or generated code.
  • Prefer auto-fixers over pure linters to minimize re-runs.
  • Cache environments in CI. Persist ~/.cache/pre-commit (or the platform cache dir) between builds.
  • Split heavy checks to pre-push or CI. Keep pre-commit fast (<2s) to maintain developer productivity.
  • Batch file I/O: tools like eslint and black support parallelism; configure them accordingly.

Tiny FAQ

  • How do I bypass a hook?

    • git commit --no-verify. Use sparingly and fix the root cause promptly.
  • How do I run only one hook?

    • pre-commit run <hook-id> --all-files or pre-commit run <hook-id>.
  • How do I skip a specific hook temporarily?

    • Use environment variable: SKIP=<hook-id> git commit -m "msg".
  • Hooks don’t run at all. Why?

    • Not installed (pre-commit install), wrong core.hooksPath, or missing .git directory.
  • How do I debug a custom shell hook?

    • Add set -euxo pipefail at the top and run bash -x .git/hooks/pre-commit.
  • Should I commit after auto-fixes?

    • Yes. Re-stage modified files and commit; that’s the intended flow.

Summary

  1. Read the error, 2) reproduce locally, 3) fix and re-stage, 4) ensure hooks are installed and executable, 5) keep hooks fast and up to date, 6) bypass only when necessary.

Series: Git

DevOps