Troubleshooting Git Pre-Commit Hook Failure (Bash) on WSL2 Ubuntu
Resolve 'pre-commit failed execution' errors for Bash Git hooks on Windows WSL2 Ubuntu, often caused by line endings or permissions.
Resolve 'pre-commit failed execution' errors for Bash Git hooks on Windows WSL2 Ubuntu, often caused by line endings or permissions.
Introduction
Encountering a "Git commit hooks pre-commit failed execution" error when working within a Windows Subsystem for Linux 2 (WSL2) environment can be a frustrating roadblock in your development workflow. This issue typically prevents git commit operations from completing successfully, often pointing to problems with the pre-commit hook script itself, particularly when dealing with Bash scripts and the nuances of cross-OS environments like Windows and Linux. This guide provides a comprehensive, expert-level approach to diagnose and resolve such failures, focusing on common pitfalls unique to WSL2.
Symptom & Error Signature
When attempting to run git commit, the operation will halt, displaying an error message similar to the following in your WSL2 terminal:
$ git commit -m "My commit message"
.git/hooks/pre-commit: 1: .git/hooks/pre-commit: #!/bin/bash: not found
.git/hooks/pre-commit: 2: Syntax error: word unexpected (expecting ")")
or
$ git commit -m "My commit message"
husky > pre-commit (node v18.17.0)
.git/hooks/pre-commit: line 7: ./scripts/run-linter.sh: Permission denied
husky > pre-commit hook failed (code 1)
or a more generic "hook failed" output:
$ git commit -m "My commit message"
husky > pre-commit (node v18.17.0)
Some error output from your script...
husky > pre-commit hook failed (code 1)
The key indicators are the "not found", "Syntax error", "Permission denied", or "hook failed (code 1)" messages originating from the pre-commit hook or a script it calls.
Root Cause Analysis
The "Git commit hooks pre-commit failed execution error" in a WSL2 Ubuntu environment, particularly for Bash scripts, almost invariably boils down to one or a combination of the following issues:
Incorrect Line Endings (CRLF vs. LF): This is by far the most common culprit. Windows uses Carriage Return and Line Feed (CRLF) for line endings, while Unix-like systems (including Ubuntu in WSL2) use only Line Feed (LF). If a Bash script is created or edited using a Windows-native editor that saves with CRLF endings, the Linux interpreter will misinterpret the
rcharacter. For instance,#!/bin/bashmight be seen as#!/bin/bashr, leading to a "command not found" or "bad interpreter" error because#!/bin/bashrdoes not exist as an interpreter.Insufficient Script Permissions: For any Bash script to be executable on Linux, it must have execute permissions (
chmod +x). If thepre-commithook (or any script it calls) lacks these permissions, the system will return a "Permission denied" error when Git tries to run it. This can occur if files are copied from Windows without retaining executable bits or if default permissions are restrictive.Incorrect Shebang (Interpreter Path): The shebang line
#!/bin/bashspecifies the interpreter for the script. Ifbashis located at a different path (e.g.,/usr/bin/bash), or if the shebang itself is malformed (often due to line ending issues), the system won't know how to execute the script.PATHEnvironment Variable Issues: Git hooks run in a relatively minimal environment. If your script relies on commands or executables that are not in the defaultPATHduring hook execution, it will fail with "command not found" errors. This is especially pertinent if the script works fine when run manually but fails as a hook.Script Syntax Errors or Runtime Failures: While less common for the "failed execution" of the hook itself (rather than the commands within it failing), a fundamental syntax error in the Bash script can prevent its initial execution. More often, a command within the hook fails (e.g., a linter or formatter), causing the hook to exit with a non-zero status, which Git interprets as a failure.
WSL2 Disk Mounts and
core.filemode: If your Git repository resides on a Windows drive (e.g.,/mnt/c/Users/YourUser/Repo) mounted within WSL2, handling file permissions can sometimes be tricky. Whilechmod +xusually works, discrepancies in how Windows and Linux handle file metadata can sometimes lead to issues. Git'score.filemodesetting can influence this, though it's less frequently the direct cause of hook execution failure itself.
Step-by-Step Resolution
Follow these steps to diagnose and resolve your Git pre-commit hook failure. Start with the most common issues first.
1. Verify and Correct Script Permissions
Ensure your pre-commit hook script, and any other scripts it calls, are executable.
- Navigate to your Git hooks directory:
cd .git/hooks - Check current permissions:
Look for anls -l pre-commitx(execute) permission for the owner (-rwxorrwxr-xr-x). If it's missing (e.g.,-rw-r--r--), you need to add execute permissions. - Add execute permissions:
chmod +x pre-commitApply
chmod +xto any other custom scripts called by yourpre-commithook (e.g.,./scripts/run-linter.shin the example error).
2. Check and Correct Line Endings (CRLF to LF)
This is often the primary culprit for "bad interpreter" or "not found" errors on the shebang line.
Check the file's line endings:
file pre-commit- Expected (correct) output:
pre-commit: Bourne-Again shell script, ASCII text executableorpre-commit: a /bin/bash script, ASCII text executable - Problematic (CRLF) output:
pre-commit: Bourne-Again shell script, ASCII text executable, with CRLF line terminatorsorpre-commit: a /bin/bash script, ASCII text executable, with CRLF line endingsThe presence of "CRLF" confirms the issue.
- Expected (correct) output:
Convert line endings from CRLF to LF: You can use
dos2unix,sed, or your text editor.Option A: Using
dos2unix(Recommended) Ifdos2unixis not installed, install it:sudo apt update sudo apt install dos2unixThen convert the file:
dos2unix pre-commitIf
pre-commitcalls other scripts, ensure those also have their line endings converted.Option B: Using
sedsed -i 's/r$//' pre-commitThis command removes carriage return characters (
r) at the end of lines.Option C: Using a Text Editor (e.g., VS Code)
- Open
pre-commitin a text editor like VS Code within WSL2 (e.g.,code .git/hooks/pre-commit). - In the bottom right status bar, click on "CRLF" and change it to "LF". Save the file.
Always check line endings for any Bash script files that are executed as part of your Git hooks, especially if they are created or edited on the Windows side.
- Open
3. Validate Shebang (Interpreter Path)
Ensure the first line of your script (#!) correctly points to the bash interpreter.
- Find the
bashinterpreter path in your WSL2 environment:
This typically returnswhich bash/usr/bin/bashor/bin/bash. - Verify the shebang line:
Open
pre-commitin a text editor (e.g.,nano .git/hooks/pre-commitorcode .git/hooks/pre-commit). The very first line should match the output ofwhich bash:
or#!/usr/bin/bash#!/bin/bashUsing
#!/usr/bin/env bashis often more portable, asenvwill search thePATHforbash.
4. Debugging the Hook Script's Content
If the hook is executable and has correct line endings/shebang, the issue lies within the script's logic.
Enable verbose debugging: Add the following lines at the beginning of your
pre-commitscript (just after the shebang):#!/bin/bash set -euxo pipefail # Add these lines for debuggingset -e: Exit immediately if a command exits with a non-zero status.set -u: Treat unset variables as an error and exit.set -x: Print commands and their arguments as they are executed.set -o pipefail: The return value of a pipeline is the status of the last command to exit with a non-zero status, or zero if all commands in the pipeline exit successfully. These flags will make your script extremely verbose, showing exactly what commands are run and where it fails.
Manually execute the hook: From your repository root, try running the hook manually:
bash .git/hooks/pre-commitObserve the output carefully for any errors, especially those indicating "command not found" or syntax issues.
Isolate problematic commands: If the script is long, comment out sections or add
echostatements to narrow down the failure point. For example:#!/bin/bash set -euxo pipefail echo "Running step 1: Linting" npx eslint . --fix echo "Step 1 complete." echo "Running step 2: Formatting" npx prettier --write . echo "Step 2 complete." # ... and so on
5. Environment and PATH Considerations
Git hooks run in a simpler environment than your interactive shell. Commands you can run manually might not be in the hook's PATH.
- Check the
PATHwithin the hook: Addecho "PATH: $PATH"to yourpre-commitscript to see what paths are available. - Use absolute paths:
Instead of
mycommand, use/usr/local/bin/mycommand. - Source your shell profile (use with caution):
If your script needs environment variables defined in your
.bashrcor.profile, you can source them, but this can slow down the hook and introduce unwanted variables.#!/bin/bash source ~/.bashrc # Or ~/.profile # Rest of your hook scriptSourcing shell profiles can sometimes introduce interactive elements or slow down your hook significantly. Prefer absolute paths or explicit variable definitions if possible.
6. WSL2 Specific: git config core.filemode
If your Git repository is located on a Windows drive mounted in WSL2 (e.g., /mnt/c/Users/YourUser/MyRepo), file permission changes made with chmod might not be persistently recognized by Git or the underlying filesystem in the way native Linux files are.
- Check repository location:
If it starts withpwd/mnt/c/(or similar), it's on a Windows drive. - Disable Git's file mode tracking:
Git typically tracks executable bit changes. On mixed filesystems, this can sometimes cause issues. Disabling it tells Git to ignore changes to the executable bit.
This command applies to the current repository. You can addgit config core.filemode false--globalto apply it to all repositories.Setting
core.filemode falsemeans Git will no longer track changes to file permissions. Be mindful of this if you have executable scripts in your repository that should have their permissions managed by Git (e.g., shell scripts, build tools).
By systematically working through these steps, you should be able to pinpoint and resolve the pre-commit hook execution failure in your WSL2 Ubuntu environment. Remember to test after each significant change.
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.