Git Hook Error: 'pre-commit hook was ignored because it's not set as executable' — Cause, Fix, and Troubleshooting Guide
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
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 directoryand 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-commitreportsExecutable \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 clonedoes not copy.git/hookscontents beyond the sample.samplefiles, and it never restores framework shims. New contributors must run the install step; document it in the README or amake setuptarget.- The executable bit is tracked oddly across filesystems. Copying a repo onto a FAT/exFAT volume, or unzipping an archive, can silently drop
+xon every hook. advice.ignoredHook falsehides 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/bashbreaks on machines where the interpreter lives elsewhere. Prefer#!/usr/bin/env bash.
Related Guides
- Git Hook Error: pre-receive hook declined
- AI-Assisted Pre-Commit Hooks for Automation Repos
- GitHub Actions: Process completed with exit code 1
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?
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.