#2113·volta

Scoped package installed via local path (`npm i -g .`) produces broken symlinks

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

Bug Description

When installing a scoped package from a local path using npm i -g ., the installed binary fails to run with No such file or directory (os error 2). The shim executes but the underlying symlink in the package image directory points to a non-existent path.

Root Cause

DirectInstall::new (crates/volta-core/src/tool/package/mod.rs) creates the staging directory with NeedsScope::No because the package name is unknown upfront. For scoped packages (e.g. @scope/pkg), the final image directory is one level deeper than the staging directory:

  • Staging: $VOLTA_HOME/tmp/image/packages/{tempdir}/lib/node_modules/@scope/
  • Final image: $VOLTA_HOME/tools/image/packages/@scope/pkg/lib/node_modules/@scope/

npm creates a relative symlink inside the staging node_modules/@scope/ pointing back to the local source directory. The relative path is computed against the staging depth. After persist_install renames the staging directory into the final image location (which is one level deeper), the relative symlink resolves to a path inside $VOLTA_HOME that does not exist.

Reproduction Steps

  1. Create a local scoped package with a bin entry, e.g. @scope/my-cli with "bin": { "my-cli": "./bin/my-cli.js" }.
  2. From the package directory, run:
    bash
    npm i -g .
  3. Attempt to run the installed binary:
    bash
    my-cli --help

Expected Behavior

The binary runs successfully.

Actual Behavior

Volta v2.0.2

Could not execute command.

See `volta help install` and `volta help pin` for info about making tools available.

Error cause: No such file or directory (os error 2)

Inspecting the symlink confirms it points to a non-existent path inside $VOLTA_HOME (the relative .. count is one short for the final image depth).

Scope

  • Affected: npm i -g . (and likely yarn global add .) with scoped local packages.
  • Not affected:
    • Non-scoped local packages (no scope nesting level involved).
    • Remote package installs (downloaded files, not symlinks).
    • npm link (uses for_npm_linkwith_name, which sets NeedsScope::Yes and matches the final depth).

Environment

  • Volta: 2.0.2
  • OS: Linux
  • Node: 20.x