#2112·volta

npx cannot discover packages installed globally via volta install

Author: BacraCreated Sep 12, 2026Updated Sep 12, 2026

Environment

  • Volta: 2.0.2
  • OS: Linux (Ubuntu)
  • Node: 20.20.2 (default, via Volta)
  • npm: 10.9.9 (default, via Volta)

Description

Packages installed globally via volta install <package> work fine when invoked directly in the shell (the shim lives in ~/.volta/bin/ which is on PATH). However, npx <command> cannot discover these commands — npx ignores packages installed globally by Volta and instead downloads a fresh copy from the registry.

Steps to reproduce

bash
# 1. Install a tool globally via Volta
volta install prettier
# package [email protected] / prettier / [email protected]

# 2. Invoke directly in the shell — works fine
prettier --version
# Output: 3.9.6 ✅

# 3. Enter an empty directory without prettier installed
mkdir /tmp/empty-project && cd /tmp/empty-project

# 4. Invoke via npx — not found, triggers a temporary download
npx prettier --version
# Prompt: "Need to install the following packages: [email protected]. Ok to proceed?" ❌

# 5. Verify with --no-install that npx indeed considers the package missing
npx --no-install prettier --version
# Error: "npx canceled due to missing packages and no YES option: [\"[email protected]\"]" ❌

Expected behavior

npx prettier should discover and reuse the prettier installed globally by Volta (consistent with how npx reuses npm install -g prettier in a non-Volta environment), avoiding a redundant download.

Actual behavior

npx reports the package as missing and attempts a temporary download from the registry.

Root cause analysis

In npm 10.9.9, the lookup logic of npx <cmd> (without --package) lives in libnpmexec/lib/index.js and consists of 4 steps — none of which search PATH for arbitrary executables:

  1. Check the bin field of the project's package.json (index.js:134-147)
  2. Check whether ./node_modules/.bin/<cmd> exists locally (index.js:150-154)
  3. Check whether ${globalBin}/<cmd> exists, where globalBin = $(npm prefix -g)/bin (index.js:155-157; defined in npm.js:453-456)
  4. Check whether the package exists in the global node_modules tree ($(npm root -g)) and its bin is present under globalBin (index.js:204-218)

If none of the 4 steps match, npx downloads the package from the registry into the npx cache.

Under Volta:

bash
npm prefix -g
# → /home/<user>/.volta/tools/image/node/20.20.2
# i.e. globalBin = /home/<user>/.volta/tools/image/node/20.20.2/bin
# This directory only contains runtime commands such as node/npm/corepack,
# not packages installed by users via volta install

ls /home/<user>/.volta/tools/image/node/20.20.2/lib/node_modules/
# → only npm / corepack, no user-installed global packages

# Packages installed via volta install actually live in:
ls ~/.volta/tools/image/packages/prettier/bin/
# → prettier (symlink → ../lib/node_modules/prettier/bin/prettier.cjs)

In other words, volta install stores global packages in ~/.volta/tools/image/packages/<pkg>/ and exposes their bins via shims in ~/.volta/bin/ (visible only to the shell via PATH). However, the locations npx relies on — $(npm prefix -g)/bin and the global node_modules tree, both of which are bound to the node image — do not contain these packages, so all 4 lookup steps miss.

For comparison: if a command happens to live in the node image's bin directory (e.g. tools installed directly via npm install -g into the node image), npx does find it via step 3 (verified: npx --no-install node --version works). This confirms the problem is not with npx's lookup mechanism itself, but that the storage location of volta install packages is invisible to npx.

Possible improvement

Expose volta install packages to $(npm prefix -g)/bin or the global node_modules tree in some way (e.g. via symlinks), so that npx's existing lookup logic can find them.