Skip to content

[BUG] npm link is broken with install-strategy=linked #9166

@manzoorwanijk

Description

@manzoorwanijk

Is there an existing issue for this?

  • I have searched the existing issues

This issue exists in the latest npm version

  • I am using the latest npm

Current Behavior

npm link (both npm link <pkg> via global and npm link <path> via local path) crashes with ENOENT when using install-strategy=linked. The linked strategy tries to extract the linked package into the .store/ directory using pacote.extract(), but the file: relative path is resolved from the store entry location (deep inside node_modules/.store/pkg@version-hash/node_modules/pkg/) instead of from the project root, resulting in a wrong absolute path.

Error output (via local path)

npm warn reify The "linked" install strategy is EXPERIMENTAL and may contain bugs.
npm error code ENOENT
npm error path <NVM_DIR>/lib/node_modules/my-linked-pkg/package.json
npm error enoent Could not read package.json

The local-path flow (npm link ../link-pkg) looks for the package in the global node_modules instead of the local path.

Error output (via global link)

npm warn tarball tarball data for my-linked-pkg@file:../../link-pkg (null) seems to be corrupted. Trying again.
npm warn tarball tarball data for my-linked-pkg@file:../../link-pkg (null) seems to be corrupted. Trying again.
npm error code ENOENT
npm error path /private/link-pkg/package.json
npm error enoent Could not read package.json

The global-link flow (npm link my-linked-pkg) tries to extract the package into .store/ using pacote.extract(), but the file: relative path is resolved from the store entry location instead of the project root, producing a wrong absolute path (/private/link-pkg instead of /private/tmp/link-pkg).

Expected Behavior

npm link should create a symlink in node_modules/ pointing to the linked package, identical to how it works with hoisted strategy. Linked packages should NOT be extracted into the .store/ — they should remain as symlinks since the whole point of npm link is to use the local source directly.

Steps To Reproduce

Via local path

rm -rf /tmp/link-pkg /tmp/link-app
mkdir -p /tmp/link-pkg /tmp/link-app

cat > /tmp/link-pkg/package.json << 'EOF'
{ "name": "my-linked-pkg", "version": "1.0.0", "main": "index.js" }
EOF
echo "module.exports = 'hello'" > /tmp/link-pkg/index.js

cat > /tmp/link-app/package.json << 'EOF'
{ "name": "link-app", "version": "1.0.0", "dependencies": { "nopt": "^7.0.0" } }
EOF

cd /tmp/link-app
npm install --install-strategy=linked --ignore-scripts
npm link ../link-pkg --install-strategy=linked
# ERROR: ENOENT Could not read package.json

Via global link

rm -rf /tmp/link-pkg /tmp/link-app
# ... (set up link-pkg and link-app as above)
cd /tmp/link-pkg && npm link           # registers globally (works)
cd /tmp/link-app && npm install --install-strategy=linked --ignore-scripts
npm link my-linked-pkg --install-strategy=linked
# ERROR: ENOENT Could not read package.json

Works with hoisted strategy

rm -rf /tmp/link-app/node_modules /tmp/link-app/package-lock.json
cd /tmp/link-app
npm install --ignore-scripts
npm link ../link-pkg   # works fine, creates symlink
node -e "console.log(require('my-linked-pkg'))"  # => "hello"

Root Cause

The isolated reifier in isolated-reifier.js treats ALL dependencies uniformly — including file: dependencies from npm link. It generates a store key and tries to extract the package into .store/pkg@version-hash/node_modules/pkg/ using pacote.extract(). The file: relative path stored in the lockfile is resolved relative to the store entry path rather than the project root, producing a wrong absolute path.

file: dependencies from npm link should be handled as symlinks (like workspace dependencies), not extracted into the store.

Environment

  • npm: 11.12.1
  • Node.js: v22.20.0
  • OS Name: macOS (Darwin 25.4.0)
  • npm config:
install-strategy=linked

Metadata

Metadata

Assignees

No one assigned

    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