Repository navigation
Description
Verify latest release
- I verified that the issue exists in the latest pnpm release
pnpm version
No response
Which area(s) of pnpm are affected? (leave empty if unsure)
No response
Link to the code that reproduces this issue or a replay of the bug
No response
Reproduction steps
mkdir esbuild-shim-repro && cd esbuild-shim-repro
echo '{ "private": true, "dependencies": { "tsx": "4.23.15" } }' > package.json
printf 'allowBuilds:\n esbuild: true\n' > pnpm-workspace.yaml
pnpm install
# The shim in the hidden hoisted directory fails:
node_modules/.pnpm/node_modules/.bin/esbuild --version
# .../node_modules/.pnpm/esbuild@0.28.2/node_modules/esbuild/bin/esbuild:1
# <ELF header bytes>
# SyntaxError: Invalid or unexpected token
# tsx's own shim for the same bin works:
node_modules/.pnpm/tsx@4.23.15/node_modules/tsx/node_modules/.bin/esbuild --version
# 0.28.2Describe the Bug
esbuild publishes bin/esbuild as a Node.js script, and its postinstall replaces that file with the native executable, so the command doesn't start Node.js on every run (esbuild docs).
After pnpm install, the shims in tsx's and esbuild's own node_modules/.bin exec the binary directly, but the one in node_modules/.pnpm/node_modules/.bin still runs it with node (POSIX lines shown):
$ grep exec node_modules/.pnpm/tsx@4.23.15/node_modules/tsx/node_modules/.bin/esbuild
exec "$basedir_abs/../../../../../esbuild@0.28.2/node_modules/esbuild/bin/esbuild" "$@"
$ grep exec node_modules/.pnpm/node_modules/.bin/esbuild
exec "$basedir/node" "$basedir_abs/../../esbuild@0.28.2/node_modules/esbuild/bin/esbuild" "$@"
exec node "$basedir_abs/../../esbuild@0.28.2/node_modules/esbuild/bin/esbuild" "$@"
Running pnpm rebuild esbuild afterwards fixes it: the hoisted shim then execs the binary directly and prints 0.28.2. So install seems to skip relinking hoisted bins after a dependency's build changes a bin target, while rebuild relinks them.
This breaks dependency build scripts, because pnpm puts node_modules/.pnpm/node_modules/.bin on their PATH. When a dependency's build script runs esbuild and esbuild is only a transitive dependency of it, the script gets the broken shim and the whole install fails. Here, dep depends on tsx, which depends on esbuild:
mkdir -p dep-repro/dep && cd dep-repro
echo '{ "private": true, "dependencies": { "dep": "file:./dep" } }' > package.json
echo '{ "name": "dep", "version": "1.0.0", "dependencies": { "tsx": "4.23.15" }, "scripts": { "postinstall": "esbuild --version" } }' > dep/package.json
printf 'allowBuilds:\n esbuild: true\n "dep@file:dep": true\n' > pnpm-workspace.yaml
pnpm install
# dep@file:dep postinstall: SyntaxError: Invalid or unexpected token
# ERR_PNPM_EXECUTOR_LIFECYCLE_SCRIPT_FAILEDTools that search .bin directories hit it too; for example, johnnyelwailer/t3code#425 worked around it on macOS.
What I checked, each with a fresh store:
- It reproduces on pnpm 12.15.0, 11.28.8, and 10.34.9, so it isn't a recent regression.
- It also reproduces on a second install from a warm store, where pnpm restores esbuild's build from the side-effects cache instead of rerunning its postinstall.
- With
publicHoistPattern: [esbuild],shamefullyHoist: true, ornodeLinker: hoisted, the rootnode_modules/.bin/esbuildworks. Only the hidden hoisted.binis affected. preferSymlinkedExecutables: trueavoids it, as does denying esbuild's build (bin/esbuildthen stays a Node.js script).
#14502 and #16594 hit the same failure with pnpm's own npm package, which also swaps a placeholder bin for a native binary during install: the shim recorded node from the placeholder's #!/usr/bin/env node line. Their fixes (#14506, #16597) removed that line from the placeholder. That changed pnpm's package, not how pnpm writes shims, so it doesn't help other packages whose install script replaces a bin, such as esbuild.
Expected Behavior
After pnpm install, node_modules/.pnpm/node_modules/.bin/esbuild should exec the native binary directly, as the other esbuild shims do and as it does after pnpm rebuild esbuild. More generally, the hoisted .bin shims of a package whose build scripts ran should be written from the bin targets as they are after the build.
Which Node.js version are you using?
24.22.1
Which operating systems have you used?
- macOS
- Windows
- Linux
If your OS is a Linux based, which one it is? (Include the version if relevant)
Ubuntu 26.04 LTS (x86_64)
Contributing a fix
- I am working on a fix
Written by an agent (Claude Code, claude-opus-5-5).
Activity
zkochan commentedon Oct 27, 2026
Opened a pull request to fix this in both pnpm v11 and pnpm v12: #17420
🤖 Generated with Antigravity
fix: refresh privately hoisted bin shims after dependency builds
fix: refresh privately hoisted bin shims after dependency builds (pnp…
Update pnpm to v12.16.1 (#2164)