Summary
vp fails to resolve node when the PATH entry containing it is relative (e.g. ./node_modules/.bin), erroring with:
error: Cannot find binary path for command 'node'
The same directory spelled as an absolute path works fine.
This isn't hypothetical: Vite+'s own git-hook runner constructs exactly such a relative entry, so vp inside a pre-commit hook fails whenever the ambient environment doesn't already provide a node of its own — which is the normal case for GUI git clients (VS Code, Tower, Fork).
Reproduction
Standalone, no hooks involved:
mkdir vp-relative-path-repro && cd vp-relative-path-repro
echo '{"name":"repro","private":true,"type":"module"}' > package.json
npm install vite-plus@0.2.7 node@24
# absolute PATH entry — works
PATH="$PWD/node_modules/.bin:/usr/bin:/bin" ./node_modules/.bin/vp check
# Found formatting issues in 1 file …
# relative PATH entry — fails
PATH="./node_modules/.bin:/usr/bin:/bin" ./node_modules/.bin/vp check
# error: Cannot find binary path for command 'node'
The only difference between the two runs is $PWD/node_modules/.bin vs ./node_modules/.bin. ./node_modules/.bin/node exists and is executable in both, and the working directory is the project root in both.
A relative entry appears to abort the search, not just be skipped
An absolute node later on PATH does not rescue it:
PATH="./node_modules/.bin:/usr/bin:/bin:/opt/homebrew/bin" ./node_modules/.bin/vp check
# error: Cannot find binary path for command 'node'
/opt/homebrew/bin/node exists (v26.5.0) and is absolute, yet lookup still fails. So the relative entry seems to abort the scan rather than be ignored or resolved against the cwd.
Why the git hooks hit this
.vite-hooks/_/h, generated by Vite+, builds its PATH from $0:
d="$(dirname "$(dirname "$(dirname "$0")")")"
export PATH="$d/node_modules/.bin:$PATH"
Git invokes hooks by a relative path (.vite-hooks/_/pre-commit), so d collapses to . and the exported entry is ./node_modules/.bin — precisely the failing shape.
Committing from a terminal usually survives, because the login shell already exports an absolute node that wins the lookup. Committing from a GUI client, whose environment carries no node, dies before the hook body runs:
[STARTED] Cleaning up temporary files...
[COMPLETED] Cleaning up temporary files...
error: Cannot find binary path for command 'node'
VITE+ - pre-commit script failed (code 1)
Expected
vp resolves node through a relative PATH entry (resolved against the current working directory, as the shell does), or at minimum skips the entry and continues scanning the rest of PATH.
Actual
Lookup fails with Cannot find binary path for command 'node', even when a valid absolute node appears later on PATH.
Environment
- vite-plus 0.2.7 (npm) — also reproduces on 0.2.5
- macOS 26.5.2, arm64
- node 24.18.0
Note: the standalone vp binary (~/.vite-plus/bin/vp) is not affected — it bundles its own node and never resolves one from PATH. Only the npm-distributed #!/usr/bin/env node build hits this, which is what a project's hooks actually run.
Workaround
Prepend an absolute entry in the hook body, ahead of the runner's relative one:
PATH="$(pwd)/node_modules/.bin:$PATH"
Suggested fix
Either or both:
- In the binary lookup, resolve relative
PATH entries against the cwd, and don't let one abort the remaining scan.
- In
h, absolutize d before exporting it.
Summary
vpfails to resolvenodewhen thePATHentry containing it is relative (e.g../node_modules/.bin), erroring with:The same directory spelled as an absolute path works fine.
This isn't hypothetical: Vite+'s own git-hook runner constructs exactly such a relative entry, so
vpinside apre-commithook fails whenever the ambient environment doesn't already provide anodeof its own — which is the normal case for GUI git clients (VS Code, Tower, Fork).Reproduction
Standalone, no hooks involved:
The only difference between the two runs is
$PWD/node_modules/.binvs./node_modules/.bin../node_modules/.bin/nodeexists and is executable in both, and the working directory is the project root in both.A relative entry appears to abort the search, not just be skipped
An absolute
nodelater onPATHdoes not rescue it:/opt/homebrew/bin/nodeexists (v26.5.0) and is absolute, yet lookup still fails. So the relative entry seems to abort the scan rather than be ignored or resolved against the cwd.Why the git hooks hit this
.vite-hooks/_/h, generated by Vite+, builds itsPATHfrom$0:Git invokes hooks by a relative path (
.vite-hooks/_/pre-commit), sodcollapses to.and the exported entry is./node_modules/.bin— precisely the failing shape.Committing from a terminal usually survives, because the login shell already exports an absolute
nodethat wins the lookup. Committing from a GUI client, whose environment carries nonode, dies before the hook body runs:Expected
vpresolvesnodethrough a relativePATHentry (resolved against the current working directory, as the shell does), or at minimum skips the entry and continues scanning the rest ofPATH.Actual
Lookup fails with
Cannot find binary path for command 'node', even when a valid absolutenodeappears later onPATH.Environment
Note: the standalone
vpbinary (~/.vite-plus/bin/vp) is not affected — it bundles its own node and never resolves one fromPATH. Only the npm-distributed#!/usr/bin/env nodebuild hits this, which is what a project's hooks actually run.Workaround
Prepend an absolute entry in the hook body, ahead of the runner's relative one:
PATH="$(pwd)/node_modules/.bin:$PATH"Suggested fix
Either or both:
PATHentries against the cwd, and don't let one abort the remaining scan.h, absolutizedbefore exporting it.