Husky hooks in GUI clients and IDEs Jump to heading
A developer commits from the terminal and the hooks run perfectly. The same developer commits from their editor’s source-control panel or a desktop Git client, and the commit fails with npx: command not found, or the hook runs with a different Node version and reports nonsense. Nothing is wrong with the hooks. GUI applications are usually started by the desktop environment, not from a shell, so they never load ~/.bashrc or ~/.zshrc — which is where Node version managers like nvm, fnm and asdf add themselves to PATH. The hook runs, cannot find Node, and fails. Husky provides a single place to fix this for every hook. This page shows how, within local hook configuration with Husky.
When to use this approach Jump to heading
- Hooks work in a terminal but fail in an editor or GUI Git client.
- Errors mention
node,npxor a tool name not being found. - Hooks run with an unexpected Node version in some clients.
- Your team uses Node version managers, which almost always means this problem.
Step 1 — Confirm it is an environment problem Jump to heading
Make the hook print its environment, then commit from both the terminal and the GUI client and compare.
# Temporarily, at the top of .husky/pre-commit
printf 'echo "PATH=$PATH" >> /tmp/hook-env.log; command -v node >> /tmp/hook-env.log 2>&1 || echo "node: not found" >> /tmp/hook-env.log\n' |
cat - .husky/pre-commit > /tmp/pc && mv /tmp/pc .husky/pre-commit After one commit from each place, /tmp/hook-env.log shows the difference — typically the terminal PATH includes something like ~/.nvm/versions/node/v20.x/bin and the GUI PATH does not. Remove the debug lines afterwards.
Step 2 — Add a Husky init script Jump to heading
Husky v9 sources ~/.config/husky/init.sh before running any hook, if the file exists. Put the version-manager setup there, once per machine, and every hook in every repository gets it.
mkdir -p ~/.config/husky
cat > ~/.config/husky/init.sh <<'EOF'
# Loaded by Husky before every hook — make Node available to GUI clients
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" --no-use
nvm use --silent >/dev/null 2>&1 || true # respects the project's .nvmrc
EOF For other version managers, the equivalent lines are short.
# fnm
eval "$(fnm env --use-on-cd)"; fnm use --silent-if-unchanged >/dev/null 2>&1 || true
# asdf
. "$HOME/.asdf/asdf.sh"
# volta
export VOLTA_HOME="$HOME/.volta"; export PATH="$VOLTA_HOME/bin:$PATH" Step 3 — Respect the project’s Node version Jump to heading
The init script should select the version the project declares, not just any version. With nvm, nvm use reads .nvmrc; with fnm and volta, the project’s .node-version or package.json volta field. Make sure the repository declares it.
node --version > .nvmrc # or set engines and a .node-version file
git add .nvmrc && git commit -m "chore: pin Node version for tooling and hooks" # Verification: commit from the GUI client and check the version the hook saw
printf 'node --version > /tmp/hook-node-version\n' >> .husky/pre-commit # temporary Step 4 — Avoid depending on PATH where possible Jump to heading
The init script fixes the environment, but hooks become more robust if they avoid needing a global node in the first place for some tools, by using paths inside the project.
# .husky/pre-commit — prefer the project-local binary
./node_modules/.bin/lint-staged This still needs node to execute the script, so it complements the init script rather than replacing it. For non-Node tools — Python linters, Go formatters — call them through the project’s environment manager, as described in lint-staged for Python and Go projects.
Step 5 — Distribute the fix to the team Jump to heading
The init script lives in each person’s home directory, so it must be set up on every machine. Add it to the developer machine bootstrap and to the contributing guide.
# In the team's machine bootstrap script
install -d ~/.config/husky
[ -f ~/.config/husky/init.sh ] || cp tooling/husky-init.sh ~/.config/husky/init.sh The broader bootstrap is covered in bootstrapping a developer machine for Git.
Step 6 — Handle Windows clients Jump to heading
On Windows, GUI clients run hooks through Git for Windows’ bundled shell. The same init script mechanism applies, but the file is under the Windows user profile, and Node installed by the official installer is usually already on the system PATH. Problems typically come from version managers such as nvm-windows, whose PATH entries are set per session.
# %USERPROFILE%\.config\husky\init.sh (read by Git for Windows' sh)
export PATH="$APPDATA/nvm/current:$PATH" # nvm-windows' active version symlink Validation checklist Jump to heading
Frequently Asked Questions Jump to heading
Why not just put the nvm lines in each hook file? Jump to heading
It works, but couples the repository to one version manager and repeats the same lines in every hook. The init script keeps machine-specific setup on the machine and hooks portable.
My editor has a setting to use a login shell. Does that help? Jump to heading
Some editors can start their integrated Git with a login shell, which loads profile files. That fixes the editor but not other GUI clients; the init script fixes all of them.
Is it safe for Husky to source a file from my home directory? Jump to heading
It runs with your permissions, like your shell profile. Keep it to environment setup; anything printed or failing there affects every hook in every repository.
Related Jump to heading
- Local Hook Configuration with Husky — the parent topic.
- Debugging lint-staged Failures — when the failure is inside the task rather than the environment.
- Signing Commits on Windows and WSL — the same environment split for signing.
- Setting Up Husky in a New Project — the baseline setup this page fixes.