Found while testing overrides in WordPress/gutenberg. We're switching to strict-peer-deps after adopting linked strategy and wanted to test react/react-dom overrides ahead of time. npm install just exits with code 1 and no error message.
Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
Adding two or more packages to overrides that share a transitive dep (e.g. react + react-dom both pull in loose-envify) causes npm install to silently exit 1 in large monorepos. No error, no ERESOLVE, nothing — just exit code 1 and a log file pointer.
Expected Behavior
Install should succeed, or show a clear error message.
Steps To Reproduce
mkdir -p repro/packages/{app,editor}
cd repro
cat > package.json << 'EOF'
{
"name": "override-repro",
"private": true,
"workspaces": ["packages/*"],
"devDependencies": {
"react": "18.3.1",
"react-dom": "18.3.1"
},
"overrides": {
"react": "$react",
"react-dom": "$react-dom"
}
}
EOF
for pkg in app editor; do
cat > "packages/$pkg/package.json" << PKGEOF
{
"name": "@repro/$pkg",
"private": true,
"dependencies": { "react": "18.3.1", "react-dom": "18.3.1" }
}
PKGEOF
done
echo 'install-strategy=linked' > .npmrc
npm install --loglevel=silly 2>&1 | grep -i conflicting
# silly Conflicting override sets <OverrideSet react> <OverrideSet react-dom>
# silly Conflicting override sets loose-envify
In this minimal repro the install finishes (exit 0) because the tree is small. In Gutenberg (hundreds of workspaces), the broken state cascades into an ERESOLVE during buildDeps, and then the error formatting crashes — so you get a silent exit 1.
Root Cause
Two bugs combine:
1. Sibling override sets treated as conflicting
findSpecificOverrideSet in override-set.js walks parent chains to see if one override set contains the other. Sibling sets (e.g. the react and react-dom children of the root override set) are never ancestors of each other, so it returns undefined. This leaves shared deps like loose-envify with an inconsistent override state. The existing haveConflictingRules check (which handles this correctly) is not used in this path.
2. ERESOLVE report crashes with depth=Infinity
explain-eresolve.js generates the full report file with depth=Infinity. In large monorepos (especially linked strategy), this recurses through the entire dep graph and eventually hits RangeError: Invalid string length. That error is thrown inside the error formatter, which swallows the original ERESOLVE — so you get exit 1 with no message at all.
Workaround
legacy-peer-deps = true in .npmrc.
Environment
- npm: 11.11.1
- Node.js: 22.20.0
- OS Name: macOS
- npm config:
install-strategy=linked, strict-peer-deps=true
Found while testing overrides in WordPress/gutenberg. We're switching to
strict-peer-depsafter adopting linked strategy and wanted to test react/react-dom overrides ahead of time.npm installjust exits with code 1 and no error message.Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
Adding two or more packages to
overridesthat share a transitive dep (e.g.react+react-domboth pull inloose-envify) causesnpm installto silently exit 1 in large monorepos. No error, no ERESOLVE, nothing — just exit code 1 and a log file pointer.Expected Behavior
Install should succeed, or show a clear error message.
Steps To Reproduce
mkdir -p repro/packages/{app,editor} cd repro cat > package.json << 'EOF' { "name": "override-repro", "private": true, "workspaces": ["packages/*"], "devDependencies": { "react": "18.3.1", "react-dom": "18.3.1" }, "overrides": { "react": "$react", "react-dom": "$react-dom" } } EOF for pkg in app editor; do cat > "packages/$pkg/package.json" << PKGEOF { "name": "@repro/$pkg", "private": true, "dependencies": { "react": "18.3.1", "react-dom": "18.3.1" } } PKGEOF done echo 'install-strategy=linked' > .npmrc npm install --loglevel=silly 2>&1 | grep -i conflicting # silly Conflicting override sets <OverrideSet react> <OverrideSet react-dom> # silly Conflicting override sets loose-envifyIn this minimal repro the install finishes (exit 0) because the tree is small. In Gutenberg (hundreds of workspaces), the broken state cascades into an ERESOLVE during
buildDeps, and then the error formatting crashes — so you get a silent exit 1.Root Cause
Two bugs combine:
1. Sibling override sets treated as conflicting
findSpecificOverrideSetinoverride-set.jswalks parent chains to see if one override set contains the other. Sibling sets (e.g. thereactandreact-domchildren of the root override set) are never ancestors of each other, so it returnsundefined. This leaves shared deps likeloose-envifywith an inconsistent override state. The existinghaveConflictingRulescheck (which handles this correctly) is not used in this path.2. ERESOLVE report crashes with
depth=Infinityexplain-eresolve.jsgenerates the full report file withdepth=Infinity. In large monorepos (especially linked strategy), this recurses through the entire dep graph and eventually hitsRangeError: Invalid string length. That error is thrown inside the error formatter, which swallows the original ERESOLVE — so you get exit 1 with no message at all.Workaround
legacy-peer-deps = truein.npmrc.Environment
install-strategy=linked,strict-peer-deps=true