Is there an existing issue for this?
This issue exists in the latest npm version
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:
Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
npm link(bothnpm link <pkg>via global andnpm link <path>via local path) crashes withENOENTwhen usinginstall-strategy=linked. The linked strategy tries to extract the linked package into the.store/directory usingpacote.extract(), but thefile:relative path is resolved from the store entry location (deep insidenode_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)
The local-path flow (
npm link ../link-pkg) looks for the package in the globalnode_modulesinstead of the local path.Error output (via global link)
The global-link flow (
npm link my-linked-pkg) tries to extract the package into.store/usingpacote.extract(), but thefile:relative path is resolved from the store entry location instead of the project root, producing a wrong absolute path (/private/link-pkginstead of/private/tmp/link-pkg).Expected Behavior
npm linkshould create a symlink innode_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 ofnpm linkis to use the local source directly.Steps To Reproduce
Via local path
Via global link
Works with hoisted strategy
Root Cause
The isolated reifier in
isolated-reifier.jstreats ALL dependencies uniformly — includingfile:dependencies fromnpm link. It generates a store key and tries to extract the package into.store/pkg@version-hash/node_modules/pkg/usingpacote.extract(). Thefile: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 fromnpm linkshould be handled as symlinks (like workspace dependencies), not extracted into the store.Environment
install-strategy=linked