Git pre-commit Hook Failed on macOS: Troubleshooting Bash Execution Errors
Troubleshoot common 'pre-commit failed execution' errors for Git bash hooks on macOS. Fix permissions, shebangs, and PATH issues causing commit failures.
Troubleshoot common 'pre-commit failed execution' errors for Git bash hooks on macOS. Fix permissions, shebangs, and PATH issues causing commit failures.
When working in a local macOS development environment, encountering a "Git pre-commit hook failed execution" error can halt your workflow. These hooks are powerful tools for enforcing code quality, style, and build constraints before a commit is even created. However, issues with their execution often stem from environmental differences, permissions, or script syntax, leading to frustrating interruptions. This guide provides a comprehensive, technical walkthrough to diagnose and resolve common bash hook execution failures specifically on macOS.
Symptom & Error Signature
When you attempt to commit changes using git commit, the process abruptly stops, displaying an error message similar to one of the following outputs. The exact message can vary depending on the specific cause and whether you're using a hook manager like Husky or a native Git hook.
Typical Error Outputs:
$ git commit -m "feat: Implement new user authentication"
husky - pre-commit hook failed (exit code 1)
Or, if the hook is a plain bash script:
$ git commit -m "fix: Resolve critical security vulnerability"
.git/hooks/pre-commit: line 3: some_linter_command: command not found
Another common failure related to permissions:
$ git commit -m "chore: Update dependencies"
git: fatal: cannot exec '.git/hooks/pre-commit': Permission denied
Sometimes, the error might be less explicit, simply stating that the hook failed:
$ git commit -m "docs: Add README updates"
Your pre-commit hook failed.
In any of these scenarios, your commit will not be created, and Git will revert to the state before the commit attempt.
Root Cause Analysis
The "pre-commit failed execution" error on macOS typically indicates that the shell script designed to run before your commit encountered a problem during its execution. Common root causes include:
- Incorrect File Permissions: The pre-commit hook script (
.git/hooks/pre-commit) might not have executable permissions. Without+x, the operating system cannot execute it. - Invalid or Missing Shebang (
#!): The very first line of a script, the shebang, tells the system which interpreter to use (e.g.,#!/bin/bashor#!/usr/bin/env node). If this line is missing, incorrect, or points to an interpreter that doesn't exist on your macOS system or is not in the hook's executionPATH, the script will fail. PATHEnvironment Variable Issues: Git hooks execute in a minimal shell environment, which often does not inherit the fullPATHvariable from your interactive shell (e.g., your~/.zshrcor~/.bashrc). This means commands likenpm,yarn,eslint,prettier, or custom binaries that are available in your regular terminal might not be found when the hook runs.- Syntax Errors in the Hook Script: Like any script, bash pre-commit hooks can contain syntax errors (e.g., typos, unclosed quotes, incorrect conditional statements) that prevent successful execution.
- Line Ending Inconsistencies: Scripts developed on Windows environments (CRLF line endings) and then used on macOS/Linux (LF line endings) can cause "command not found" errors or unexpected behavior due to the carriage return character (
r) being interpreted as part of a command or shebang. - External Tool Dependencies Not Installed: If the hook relies on external tools (e.g.,
jq,grep, specific linters, or Node.js packages) that are not installed globally or locally in the project, the commands will naturally fail. - Hook Manager Configuration (e.g., Husky): If using a tool like Husky, its configuration (
.husky/pre-commit) might be pointing to an incorrect script, or Husky itself might not be correctly installed or initialized within the project.
Step-by-Step Resolution
Follow these steps to systematically diagnose and resolve your Git pre-commit hook execution error on macOS.
1. Locate the Pre-commit Hook Script
First, identify the exact location of the problematic hook.
- Native Git Hook: The script is typically located at
.git/hooks/pre-commitwithin your repository. - Husky (or similar manager): The script will usually be in
.husky/pre-commit. Husky creates a thin wrapper in.git/hooks/pre-committhat points to the.huskydirectory.
Use ls -la to verify its existence and initial permissions:
ls -la .git/hooks/pre-commit
# Or for Husky:
ls -la .husky/pre-commit
2. Verify and Correct Script Permissions
The hook script must be executable. If you see something like -rw-r--r-- (read/write only) for the owner, you need to add execute permissions.
# For native Git hook
chmod +x .git/hooks/pre-commit
# For Husky hook
chmod +x .husky/pre-commit
After changing permissions, try
git commitagain. This resolves a significant percentage of "Permission denied" errors.
3. Inspect the Shebang Line
Open the hook script with your preferred text editor (e.g., nano, vim, code). The very first line should be the shebang.
# Open the hook script
nano .git/hooks/pre-commit
# Or for Husky
nano .husky/pre-commit
Look for the first line. It should typically be:
#!/bin/bash
or
#!/usr/bin/env bash
or for Node.js scripts:
#!/usr/bin/env node
#!/bin/bash: Points directly to the bash interpreter. Ensure/bin/bashexists on your system (it almost always does on macOS).#!/usr/bin/env bash: Usesenvto find thebashexecutable in the currentPATH. This is generally more portable as it doesn't hardcode the interpreter's location.- Incorrect path: If it's
#!/bin/shand the script uses bash-specific features, it might fail. If it points to a non-existent path (e.g.,#!/usr/local/bin/bashwhen bash is somewhere else), it will fail. - Missing shebang: If the line is entirely absent, the shell might try to execute it with the default shell (often
zshon modern macOS), leading to syntax errors if the script is written forbash.
Ensure there are no spaces or extra characters before the
#!. It must be the absolute first characters in the file.
4. Debug PATH Environment Variables
This is a very common cause of "command not found" errors. Git hooks run in a stripped-down environment.
a. Echo PATH within the hook:
Temporarily add echo "PATH: $PATH" and which <command_that_failed> inside your hook script, just after the shebang, to see what PATH it's using and where it's looking for the command.
#!/bin/bash
echo "Current PATH in hook: $PATH"
which npm
which eslint
# Rest of your hook script...
npm run lint
Commit temporarily to trigger the hook and observe the output. This will reveal if npm or eslint are found.
b. Explicitly set PATH or source your profile:
If commands are not found, you have a few options:
Use absolute paths: Replace
npmwith/usr/local/bin/npmor/opt/homebrew/bin/npm(depending on wherewhich npmtells you it is). This is robust but less flexible.Extend
PATHin the hook: Add necessary paths at the beginning of your hook script.#!/bin/bash # Ensure Homebrew paths are included for common tools export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:$PATH" # For Node.js packages installed via nvm, you might need to load nvm # This is more complex and depends on your nvm setup # export NVM_DIR="$HOME/.nvm" # [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm # [ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion" # This loads nvm bash_completion # Now your commands should work npm run lintSource your shell profile (caution advised): While tempting, directly sourcing
~/.zshrcor~/.bashrcinside a hook can introduce environmental side effects or slow down the hook. It's generally better to explicitly define necessary paths or variables.# This is generally discouraged for performance and environment isolation, # but can be a quick fix for testing # source ~/.zshrc || source ~/.bashrc
5. Manually Execute and Debug the Script
Run the hook script directly from your terminal to get verbose output and isolate the exact failing command.
# Navigate to your project root
cd /path/to/your/repo
# Execute the hook with bash, enabling verbose tracing (-x)
bash -x .git/hooks/pre-commit
# Or for Husky
bash -x .husky/pre-commit
The -x option will print each command and its arguments after expansion, making it easier to pinpoint where the script fails.
6. Check for Script Syntax Errors
Use bash -n (no execution) to check for syntax errors in your script without actually running it.
bash -n .git/hooks/pre-commit
# Or for Husky
bash -n .husky/pre-commit
If there are syntax errors, bash -n will output them.
7. Address Line Ending Issues (CRLF vs LF)
If the script was created or edited on a Windows machine and then used on macOS, it might have CRLF (Carriage Return Line Feed) line endings, which bash interprets differently.
a. Check line endings:
file .git/hooks/pre-commit
Look for CRLF in the output. If it says text or text executable, it's likely LF. If it says with CRLF line terminators, then you have an issue.
b. Convert line endings:
Use dos2unix if available (installable via Homebrew: brew install dos2unix):
dos2unix .git/hooks/pre-commit
# Or for Husky
dos2unix .husky/pre-commit
Alternatively, use sed:
sed -i '' 's/r$//' .git/hooks/pre-commit
8. Re-initialize or Verify Hook Manager Setup (e.g., Husky)
If you're using Husky, ensure it's correctly installed and configured for your project.
Ensure Husky is installed: Check
package.jsonforhuskydependency.Re-run Husky install: This ensures the
.git/hookssymbolic link is correctly set up.# From your project root npm install npx husky installIf your
package.jsonhas apreparescript,npm install(oryarn install) should triggerhusky installautomatically.Check Husky hook content: The
.husky/pre-commitscript itself is usually quite simple, typically just executing a command defined in yourpackage.jsonor another script. For example:#!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npm test npm run lintEnsure
npm testornpm run lintare valid commands that execute successfully outside the hook.
By systematically working through these steps, you should be able to pinpoint the exact cause of your Git pre-commit hook failure on macOS and restore your commit workflow.
Our Production Verification Guarantee
Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.