Found while testing overrides in WordPress/gutenberg. We're switching to strict-peer-deps after adopting the linked strategy.
Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
Somewhat related to #9109
When using install-strategy=linked, npm overrides for transitive dependencies are ignored. The overridden version is installed but the parent's edge still expects the lockfile version, marking the overridden package as invalid. With strict-peer-deps=true, this causes an ERESOLVE error and the install fails completely.
With the default hoisted strategy, the same overrides work correctly — the overridden packages are installed and marked as overridden in the tree.
Expected Behavior
Overrides should work the same way regardless of install strategy. The overridden version should satisfy the parent's dependency edge, just like it does with hoisted.
Steps To Reproduce
Setup (common to both strategies)
rm -rf /tmp/npm-override-bug
mkdir -p /tmp/npm-override-bug
cd /tmp/npm-override-bug
cat > package.json << 'EOF'
{
"name": "override-bug-repro",
"version": "1.0.0",
"private": true,
"devDependencies": {
"typescript": "5.8.3",
"@typescript-eslint/utils": "8.51.0"
}
}
EOF
With linked strategy (BUG)
echo 'install-strategy=linked' > .npmrc
# 1. Install to create lockfile with 8.51.0 versions
npm install
# 2. Upgrade to TS 6 and add overrides for transitive deps
cat > package.json << 'EOF'
{
"name": "override-bug-repro",
"version": "1.0.0",
"private": true,
"devDependencies": {
"typescript": "6.0.2",
"@typescript-eslint/utils": "8.51.0"
},
"overrides": {
"@typescript-eslint/typescript-estree": "8.58.0",
"@typescript-eslint/project-service": "8.58.0"
}
}
EOF
# 3. Reinstall — override is applied but tree is invalid
npm install
npm ls @typescript-eslint/typescript-estree
# Actual: @typescript-eslint/typescript-estree@8.58.0 invalid: "8.51.0"
# Expected: @typescript-eslint/typescript-estree@8.58.0 overridden
With hoisted strategy (WORKS)
rm -rf /tmp/npm-override-bug
mkdir -p /tmp/npm-override-bug
cd /tmp/npm-override-bug
# ... same setup as above, without the .npmrc ...
npm install
# ... same package.json change as above ...
npm install
npm ls @typescript-eslint/typescript-estree
# @typescript-eslint/typescript-estree@8.58.0 overridden ✓
With strict-peer-deps=true
Adding strict-peer-deps=true to the .npmrc makes it worse — step 3 fails outright with ERESOLVE, and the override is completely ignored:
npm error ERESOLVE could not resolve
npm error While resolving: @typescript-eslint/project-service@8.51.0
npm error Found: typescript@6.0.2
npm error Could not resolve dependency:
npm error peer typescript@">=4.8.4 <6.0.0" from @typescript-eslint/project-service@8.51.0
Cleanup
rm -rf /tmp/npm-override-bug
Root Cause Analysis
During loadVirtual in load-virtual.js, nodes loaded from the lockfile are created without override context. When the linked strategy builds the tree, override propagation through parent edges doesn't properly update the edge's expected spec, so the overridden version is treated as invalid rather than overridden.
In hoisted mode, when an invalid edge is detected after loading the virtual tree, it gets queued for re-resolution in #initTree (build-ideal-tree.js ~line 340). The linked strategy appears to handle these differently, skipping re-resolution for overridden transitive deps.
Environment
- npm: v11.12.1 (fork, latest branch)
- Node.js: v24.14.0
- OS: macOS (Darwin 25.4.0)
- npm config:
install-strategy=linked
strict-peer-deps=true ; (optional, makes it worse)
Found while testing overrides in WordPress/gutenberg. We're switching to
strict-peer-depsafter adopting the linked strategy.Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
Somewhat related to #9109
When using
install-strategy=linked, npm overrides for transitive dependencies are ignored. The overridden version is installed but the parent's edge still expects the lockfile version, marking the overridden package asinvalid. Withstrict-peer-deps=true, this causes anERESOLVEerror and the install fails completely.With the default
hoistedstrategy, the same overrides work correctly — the overridden packages are installed and marked asoverriddenin the tree.Expected Behavior
Overrides should work the same way regardless of install strategy. The overridden version should satisfy the parent's dependency edge, just like it does with
hoisted.Steps To Reproduce
Setup (common to both strategies)
With linked strategy (BUG)
With hoisted strategy (WORKS)
With
strict-peer-deps=trueAdding
strict-peer-deps=trueto the.npmrcmakes it worse — step 3 fails outright withERESOLVE, and the override is completely ignored:Cleanup
Root Cause Analysis
During
loadVirtualin load-virtual.js, nodes loaded from the lockfile are created without override context. When the linked strategy builds the tree, override propagation through parent edges doesn't properly update the edge's expected spec, so the overridden version is treated as invalid rather than overridden.In
hoistedmode, when an invalid edge is detected after loading the virtual tree, it gets queued for re-resolution in#initTree(build-ideal-tree.js ~line 340). The linked strategy appears to handle these differently, skipping re-resolution for overridden transitive deps.Environment