Visitar URL original
`pnpm setup` leaves a broken `$PNPM_HOME/bin/pnpm` when pnpm 12 was installed with npm on macOS (executable path is not canonicalized) · Issue #16855 · pnpm/pnpm · GitHub
Skip to content

pnpm setup leaves a broken $PNPM_HOME/bin/pnpm when pnpm 12 was installed with npm on macOS (executable path is not canonicalized) #16855

Closed

Description

@antst

Verify latest release

  • I verified that the issue exists in the latest pnpm release

pnpm version

12.11.2 (the latest dist-tag on 2026-10-11). The code cited below is unchanged at v12.12.1 and on main at 77186e6.

Which area(s) of pnpm are affected? (leave empty if unsure)

CLI, Operating System (Windows, MacOS, Linux)

Link to the code that reproduces this issue or a replay of the bug

https://github.com/antst/dashi/actions/runs/38143574787/job/114482195699 (GitHub-hosted macos-26-arm64 runner; the complete command log is the job's install-macos-latest-G-pnpm12 artifact)

Reproduction steps

On macOS, with Node.js and npm:

T="$(mktemp -d)"; mkdir "$T/user" "$T/tools" "$T/pnpm-global"
export HOME="$T/user" PNPM_HOME="$T/pnpm-global" SHELL=/bin/bash
npm install --global --prefix "$T/tools" pnpm@12.11.2
export PATH="$T/tools/bin:$PATH"
pnpm setup
. "$HOME/.bashrc"
pnpm --version

These are the steps of the CI job linked above, with these differences: the job's $T was a directory under the runner's temp directory, its first command after setup was pnpm add -g @deepseek-ai/dsh@0.2.1-alpha.2 rather than pnpm --version, and it also pointed XDG_* and npm's cache, userconfig and prefix into the same tree. The failure is in the launcher's exec, before pnpm itself starts. On Linux this snippet, run verbatim, ends by printing 12.11.2.

The consequence can be reproduced on Linux without a Mac by running setup's self-install step by hand against the bin directory, which is the directory macOS makes setup use (run verbatim on Ubuntu 24.04, Node.js 25.9.0, npm 11.12.1):

T="$(mktemp -d)"; mkdir "$T/user" "$T/tools" "$T/pnpm-global"
export HOME="$T/user" PNPM_HOME="$T/pnpm-global"
npm install --global --prefix "$T/tools" pnpm@12.11.2
export PATH="$T/tools/bin:$PATH"
echo '{"name":"pnpm","version":"12.11.2","type":"module","bin":{"pnpm":"pnpm","pn":"pnpm"},"files":["pnpm","dist/"]}' > "$T/tools/bin/package.json"
PATH="$PNPM_HOME/bin:$PATH" pnpm add -g --ignore-scripts "file:$T/tools/bin"
rm "$T/tools/bin/package.json"
"$PNPM_HOME/bin/pnpm" --version; echo "exit=$?"
ls -A "$PNPM_HOME"/global/v11/*/node_modules/pnpm/

The install prints + pnpm file:../../../../tools/bin, the same line as on macOS. The launcher then fails with the following, where <tmp> stands for $T: <tmp>/pnpm-global/bin/pnpm: 82: exec: <tmp>/pnpm-global/bin/../global/v11/82859147b4fec5e788f016e5ca7f0a52c473a92cd2037e4c8bdc7029c86b60f3/node_modules/pnpm/pnpm: not found and exit=127 (the launcher is a /bin/sh script, here dash), and the installed node_modules/pnpm/ contains only package.json and node_modules.

Describe the Bug

On macOS (26.6.2, arm64; Node.js 22.23.2, npm 10.9.8) pnpm setup exits 0 and prints the following. As in the steps above, <tmp>/tools is the npm prefix, <tmp>/pnpm-global is PNPM_HOME and <tmp>/user is HOME; only that temporary prefix is shortened.

Installing pnpm CLI globally from <tmp>/tools/bin
.../global/v11/f26-18dd7cebb67cf618-0    | Progress: resolved 1, reused 0, downloaded 0, added 0
Packages are hard linked from the content-addressable store to the virtual store.
  Content-addressable store is at: <tmp>/pnpm-global/store/v11
  Virtual store is at:             ../pnpm-global/global/v11/f26-18dd7cebb67cf618-0/node_modules/.pnpm
.../global/v11/f26-18dd7cebb67cf618-0    | Progress: resolved 1, reused 0, downloaded 0, added 1, done

dependencies:
+ pnpm file:../../../../tools/bin

Done in 112ms using pnpm v12.11.2
Created <tmp>/user/.bashrc

The following configuration changes were made:
export PNPM_HOME='<tmp>/pnpm-global'
case ":$PATH:" in
  ":$PNPM_HOME/bin:"*) ;;
  *) export PATH="$PNPM_HOME/bin:$PATH" ;;
esac

To start using pnpm, run:
source <tmp>/user/.bashrc

After sourcing that file, the first pnpm command (pnpm add -g @deepseek-ai/dsh@0.2.1-alpha.2 in the job) exits with status 126:

<tmp>/pnpm-global/bin/pnpm: line 82: <tmp>/pnpm-global/bin/../global/v11/82859147b4fec5e788f016e5ca7f0a52c473a92cd2037e4c8bdc7029c86b60f3/node_modules/pnpm/pnpm: No such file or directory
<tmp>/pnpm-global/bin/pnpm: line 82: exec: <tmp>/pnpm-global/bin/../global/v11/82859147b4fec5e788f016e5ca7f0a52c473a92cd2037e4c8bdc7029c86b60f3/node_modules/pnpm/pnpm: cannot execute: No such file or directory

The generated launcher comes first on PATH, so every later pnpm command fails, while the npm-installed binary still works by its full path (<tmp>/tools/bin/pnpm --version printed 12.11.2 in the same job).

On Linux the same steps work. Setup reports the real package directory (Installing pnpm CLI globally from <tmp>/tools/lib/node_modules/pnpm, + pnpm file:../../../../tools/lib/node_modules/pnpm), downloads @pnpm/exe.linux-x64@12.11.2, and the generated launcher prints 12.11.2.

What the source at v12.11.2 shows:

  • current_executable is std::env::current_exe (pnpm/crates/executor/src/pnpm_executable.rs:3-4), which on macOS is the path pnpm was started through. npm's global bin entry <prefix>/bin/pnpm is a symlink to ../lib/node_modules/pnpm/pnpm.
  • setup uses that path's parent as the package directory (pnpm/crates/cli/src/cli_args/setup.rs:62-68 and :99-116). <prefix>/bin has no package.json, so setup writes its standalone manifest (:169-177) there, runs pnpm add -g --ignore-scripts file:<prefix>/bin (:138-142) and removes the manifest again (:146).
  • The package installed from that directory has no pnpm file (see the Linux run above), but the launcher that execs node_modules/pnpm/pnpm is still generated. We did not look into why the symlinked pnpm entry is left out of the installed copy.

This looks like the same root cause as #15694 and #15691, which #15695 and #15692 fixed for two other users of current_exe.

For context, pnpm 11.18.0 in the same job layout also reported Installing pnpm CLI globally from <tmp>/tools/bin, on macOS and on Linux, then warned Failed to create bin at <tmp>/pnpm-global/bin/pnpm. ENOENT: no such file or directory, open …, and the later pnpm commands of that job worked. We did not check whether a launcher existed there, so we cannot say that 11.x produced a working one; that is why this is a plain bug report and not a regression report.

Workaround: do not run pnpm setup; keep using the npm-installed pnpm and set the global bin directory by hand (pnpm config set global-bin-dir <dir>, with <dir> on PATH). The same macOS job passes that way: https://github.com/antst/dashi/actions/runs/38152725106 (install-layouts-macos / G-pnpm12 / macos-latest).

We tested only an npm install with --prefix. We did not test a default-prefix npm install -g pnpm or a Homebrew install; we expect any install where pnpm is reached through a symlink to take the same path.

Expected Behavior

pnpm setup installs from the real package directory, as it does on Linux, so that $PNPM_HOME/bin/pnpm runs. Failing that, it should exit with an error rather than exit 0 and leave a launcher that cannot run.

Suggested fix: canonicalize the executable path before taking its parent in install_cli_globally (setup.rs:99-101), for example with dunce::canonicalize as #15695 and #15692 did.

Which Node.js version are you using?

22.23.2 (macOS); 25.9.0 (Linux by-hand run)

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 24.04. pnpm setup itself works there; Linux was used for the control run and the by-hand reproduction.

Contributing a fix

  • I am working on a fix

Activity

  1. zkochan commented on Oct 11, 2026

    @zkochan
    Member

    Thanks for the detailed report. #16857 fixes this: pnpm setup now resolves the executable through symlinks before choosing the directory to install from, so an npm install on macOS installs from <prefix>/lib/node_modules/pnpm as on Linux. The same PR fixes pnpm 11, where process.argv[1] kept the <prefix>/bin/pnpm symlink path; an npm-installed pnpm 11 now skips the self-install it could not complete. It covers the whole issue.


    Written by an agent (Claude Code, claude-opus-5-5).

  2. self-assigned this
    on Oct 11, 2026
  3. added a commit that references this issue on Oct 11, 2026
    8c47795
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions