Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for Automation By James Joyner IV · · 9 min read Last reviewed Jul 2026

Git Hook Error: 'pre-commit hook was ignored because it's not set as executable' — Cause, Fix, and Troubleshooting Guide

Quick answer

Fix Git's 'pre-commit hook was ignored because it's not set as executable' and 'cannot run .git/hooks/pre-commit' — permissions, hooksPath, shebang, CRLF.

  • #automation
  • #troubleshooting
  • #git
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

Git 2.36 and later refuse to run a hook that exists but is not marked executable, and instead of silently skipping it, they print a warning so you notice the misconfiguration. The commit still succeeds — but with none of the validation the hook was supposed to enforce:

$ git commit -m "add parser"
hint: The '.git/hooks/pre-commit' hook was ignored because it's not set as executable.
hint: You can disable this warning with `git config advice.ignoredHook false`.
[main 7f3a1c2] add parser
 1 file changed, 14 insertions(+)

A closely related failure happens when the file is executable but cannot actually be run — a missing interpreter or a bad path produces a hard error that aborts the commit:

$ git commit -m "add parser"
error: cannot run .git/hooks/pre-commit: No such file or directory

And when you use the pre-commit framework (pre-commit.com) rather than a raw hook, a misconfigured or uninstalled tool shows up as:

Executable `black` not found
Check the log at ~/.cache/pre-commit/pre-commit.log

All three point at the same class of problem: the hook is present in intent but the shell cannot locate, execute, or interpret it. The dangerous case is the first one — the commit lands with no checks run at all.

Symptoms

  • hint: The '.git/hooks/pre-commit' hook was ignored because it's not set as executable. on every commit.
  • error: cannot run .git/hooks/pre-commit: No such file or directory and the commit is aborted.
  • The hook “used to work” and stopped after a fresh clone, a cp, or a checkout on a different filesystem.
  • Formatting/linting that should block a commit no longer runs, yet nobody edited the hook.
  • pre-commit reports Executable \X` not foundorcommand not found` for a tool you believe is installed.

Common Root Causes

1. The hook is missing the executable bit

The most common cause. Git 2.36+ checks the +x bit; if it is not set, the hook is ignored with the advice.ignoredHook warning. Copying a hook with cp, writing it from an editor, or restoring it from an archive frequently drops the executable bit.

2. core.hooksPath points somewhere else

If core.hooksPath is set (globally or per-repo), Git looks in that directory, not .git/hooks. A hook you edited in .git/hooks is never consulted, so it “does nothing” no matter how you chmod it.

3. Wrong shebang or a missing interpreter

The hook is executable, but its first line names an interpreter that does not exist on this machine (#!/bin/bash on a system with bash only in /usr/bin, or #!/usr/bin/env python3 with no python3 on PATH). The kernel cannot exec it, producing cannot run ... : No such file or directory — which refers to the interpreter, not the hook file.

4. Hooks were never installed by the framework

With pre-commit, husky, or similar, the .git/hooks/pre-commit shim only exists after you run the install step. A fresh clone has the config file (.pre-commit-config.yaml) but no hook, so nothing runs.

5. Windows CRLF line endings in the shebang

A hook saved with CRLF endings makes the shebang read #!/bin/sh\r. The kernel tries to exec an interpreter literally named /bin/sh\r, which does not exist:

error: cannot run .git/hooks/pre-commit: No such file or directory

6. The named tool is not on PATH

The framework runs but a hook entry (black, flake8, shellcheck) is not installed or not on the PATH the hook sees, yielding Executable \X` not found`.

How to Diagnose

Start by confirming which hook Git is actually using. If core.hooksPath is set, .git/hooks is irrelevant:

git config --get core.hooksPath
git config --show-origin --get core.hooksPath
/home/myuser/.config/git/hooks
file:/home/myuser/.gitconfig    /home/myuser/.config/git/hooks

Now list the hook directory that is actually in effect and check the permission bits:

ls -l .git/hooks/pre-commit
-rw-r--r-- 1 myuser myuser 240 Jul 12 09:14 .git/hooks/pre-commit

No x in -rw-r--r-- confirms the “not set as executable” warning. Contrast with a working hook, which shows -rwxr-xr-x.

Verify the interpreter the shebang names actually exists:

head -1 .git/hooks/pre-commit
file .git/hooks/pre-commit
command -v bash python3
#!/usr/bin/env python3
.git/hooks/pre-commit: a /usr/bin/env python3 script, ASCII text executable
/usr/bin/bash

Here python3 is absent from the output of command -v, so the exec fails even though the file is executable.

Check for hidden CRLF endings, which ls and a casual cat will not reveal:

file .git/hooks/pre-commit
cat -A .git/hooks/pre-commit | head -1
.git/hooks/pre-commit: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
#!/bin/sh^M$

The trailing ^M$ (carriage return before the newline) is the smoking gun.

For framework hooks, confirm the shim is installed and the tool resolves:

cat .git/hooks/pre-commit | head -3
pre-commit --version
command -v black
#!/usr/bin/env bash
# File generated by pre-commit: https://pre-commit.com
# ID: 138fd403232d2ddd5efb44317e38bf03
pre-commit 3.7.1

If the third command prints nothing, black is not on PATH and you will get Executable \black` not found`.

Fixes

Restore the executable bit — this clears the “ignored because it’s not set as executable” warning:

chmod +x .git/hooks/pre-commit
ls -l .git/hooks/pre-commit
-rwxr-xr-x 1 myuser myuser 240 Jul 12 09:14 .git/hooks/pre-commit

If core.hooksPath is set and you did not intend it, unset it or edit the hook in the directory it points at:

git config --unset core.hooksPath        # per-repo
git config --global --unset core.hooksPath

Fix a broken shebang so it names an interpreter that exists, and make sure that interpreter is installed:

# Use env for portability instead of a hard-coded path
sed -i '1s|.*|#!/usr/bin/env bash|' .git/hooks/pre-commit
command -v bash   # confirm it resolves

Strip CRLF line endings introduced on Windows:

dos2unix .git/hooks/pre-commit
# or, without dos2unix:
sed -i 's/\r$//' .git/hooks/pre-commit
file .git/hooks/pre-commit
.git/hooks/pre-commit: Bourne-Again shell script, ASCII text executable

Install framework hooks so the shim actually exists in every clone:

pip install pre-commit          # or: pipx install pre-commit
pre-commit install
pre-commit run --all-files       # verify the tools resolve
pre-commit installed at .git/hooks/pre-commit
black....................................................................Passed

For Executable \X` not found, install the missing tool or let the framework manage it in its own isolated environment (the repo:+rev:form in.pre-commit-config.yaml) instead of a systemlanguage entry that depends on yourPATH`.

What to Watch Out For

  • The “ignored because not executable” warning does not fail the commit — CI may be the only thing catching what the skipped hook would have. Treat the hint as an error in spirit.
  • git clone does not copy .git/hooks contents beyond the sample .sample files, and it never restores framework shims. New contributors must run the install step; document it in the README or a make setup target.
  • The executable bit is tracked oddly across filesystems. Copying a repo onto a FAT/exFAT volume, or unzipping an archive, can silently drop +x on every hook.
  • advice.ignoredHook false hides the warning but does not fix anything — only use it once you have deliberately disabled a hook.
  • A hard-coded shebang path like #!/usr/local/bin/bash breaks on machines where the interpreter lives elsewhere. Prefer #!/usr/bin/env bash.
Free download · 368-page PDF

Fixed it? Get 500 Automation & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.