Fix npm ERESOLVE Peer Dependency Conflicts: Root Cause and Minimal Fixes
Quick Answer
- Conclusion: npm ERESOLVE errors occur when installed packages request peer dependencies that conflict with versions already in the dependency tree. The fix is to diagnose the conflict chain with
npm explain, then either upgrade the parent package or add anoverridesblock inpackage.json. - First checks: Run
npm install 2>&1 | head -80to see the full error, thennpm explain <conflicting-package>to trace the conflict chain. Check if the parent package has a newer version that supports your current dependency versions. - Minimal fix: If upgrading the parent package is possible, run
npm install <parent>@latest. Otherwise, add anoverridesblock topackage.json, deletenode_modulesandpackage-lock.json, then reinstall. - Version boundary: npm
overridesrequires npm >= 8.3.0. Older versions silently ignore the field. For npm 7.x, use--legacy-peer-depsas a temporary workaround only.
When This Error Appears
The ERESOLVE error typically surfaces in these scenarios:
- Framework upgrades: Upgrading React, Vue, or Angular to a new major version while some dependencies still require the old version as a peer dependency.
- Monorepo projects: Multiple sub-packages depend on different versions of the same library, causing hoisting conflicts.
- CI/CD pipelines:
npm cifails because the lockfile is out of sync withpackage.jsonafter dependency changes. - Team collaboration: Different developers use different package manager versions, leading to inconsistent dependency trees.
Root Cause Analysis
npm 7+ introduced strict peer dependency resolution by default. When npm encounters a package that declares a peer dependency on version X, but another package in the tree already installed version Y (where X and Y are incompatible), npm throws an ERESOLVE error instead of silently proceeding.
The conflict chain typically looks like:
package-a@1.0.0
└── peer dependency: react@"^17.0.0"
package-b@2.0.0
└── dependency: react@"^18.0.0" (already installed)
npm cannot satisfy both constraints simultaneously, so it fails.
Diagnostic Commands
Before applying any fix, diagnose the exact conflict:
BASH# See the full error message npm install 2>&1 | head -80 # Trace the conflict chain for a specific package npm explain <conflicting-package> # List the entire dependency tree npm ls --all --depth=5 # Check what versions are available for the parent package npm view <parent-package> versions --json
For yarn users, the equivalent commands are:
BASHyarn why <package> yarn info <package> versions
For pnpm users:
BASHpnpm why <package> pnpm ls --depth=5
Minimal Working Configuration
Fix 1: Upgrade the Parent Package (Preferred)
BASH# Check available versions npm view <parent-package> versions --json # Upgrade to the latest version npm install <parent-package>@latest # If that doesn't work, try a specific version npm install <parent-package>@<specific-version>
Fix 2: Use overrides (npm >= 8.3.0)
Add to package.json:
JSON{ "overrides": { "react": "^18.0.0", "react-dom": "^18.0.0" } }
For more specific overrides targeting a particular dependency chain:
JSON{ "overrides": { "some-package": { "react": "^18.0.0" } } }
Then clean and reinstall:
BASHrm -rf node_modules package-lock.json npm install
Fix 3: Temporary Workaround (Not for Production)
BASH# npm 7.x only - skip peer dependency checks npm install --legacy-peer-deps # npm 8+ - force installation npm install --force
Warning: These flags hide real compatibility issues and can cause runtime errors. Never use them in CI/CD or production environments.
Common Errors and Fixes
| Error | Solution |
|---|---|
ERESOLVE unable to resolve dependency tree | Run npm install 2>&1 | head -80 for full error, then npm explain <conflicting-package> to trace the chain. Upgrade parent or add overrides. |
Could not resolve dependency: peer X@^Y from Z | Use npm explain <package> to see the chain. Upgrade package Z or add overrides for X. |
npm WARN ERESOLVE overriding peer dependency | Check with npm ls <package> if the override is intentional. If expected, ignore the warning; otherwise remove the override and fix the root cause. |
npm ci fails but npm install works locally | Lockfile is out of sync. Run rm -rf node_modules package-lock.json && npm install locally, commit the regenerated lockfile, then re-run CI. |
Production Notes and Security Checks
Critical Limitations
- Root-only overrides: npm and yarn overrides/resolutions only work in the root
package.json. Workspace sub-package overrides are ignored. - Clean reinstall required: After modifying overrides, you must delete
node_modulesand lockfiles, then reinstall. Otherwise, the overrides may not take effect. - No legacy flags in production:
--legacy-peer-depsand--forcehide real compatibility issues and should never be used in CI/CD or production. - pnpm behavior: pnpm does not auto-install conflicting peer dependencies. It prints warnings and skips them, requiring manual handling via
pnpm.peerDependencyRules. - npm version requirement:
overridesrequires npm >= 8.3.0. Older versions silently ignore the field.
Security Recommendations
- Run
npm auditregularly to check dependency security. - Use
npm ciin CI pipelines instead ofnpm installfor deterministic builds. - When overriding third-party packages, ensure the replacement version has been security-reviewed.
- Avoid setting
legacy-peer-deps=truein global.npmrcfiles.
Comparison With Alternatives
| Dimension | npm | yarn | pnpm |
|---|---|---|---|
| Override mechanism | overrides in package.json | resolutions in package.json | overrides + peerDependencyRules |
| Diagnostic tool | npm explain, npm ls | yarn why | pnpm why |
| Force install flag | --legacy-peer-deps, --force | --ignore-engines | --strict-peer-dependencies false |
| Workspace support | Root-only overrides | Root-only resolutions | Supports granular rules per workspace |
| Peer dependency handling | Strict by default (npm 7+) | Strict by default (yarn 2+) | Warns and skips by default |
The key advantage of this diagnostic-first approach is that it works across all major package managers, emphasizing root cause resolution over force-installing problematic dependencies.
FAQ
Q: Why does --legacy-peer-deps succeed but cause runtime errors?
A: --legacy-peer-deps skips peer dependency version checks, allowing incompatible dependency combinations to install. This can cause runtime type errors, missing API calls, or broken functionality. For example, if a React component library requires react@17 but your project uses react@18, components using deprecated APIs may fail. The correct approach is to diagnose the conflict with npm explain and resolve it explicitly through upgrades or overrides.
Q: Why don't overrides in workspace sub-packages work?
A: npm and yarn overrides/resolutions only take effect in the root package.json. For workspace sub-packages, hoisted dependencies are lifted to the root node_modules, so sub-package overrides are ignored. Always place all overrides in the root package.json. After modifying them, perform a full clean reinstall from the workspace root (delete all node_modules and lockfiles) to ensure the overrides take effect.
Q: How does pnpm handle peer dependency conflicts differently from npm and yarn?
A: pnpm does not auto-install conflicting peer dependencies. When it detects a conflict, it prints a warning and skips the installation, rather than throwing an ERESOLVE error like npm. Developers must handle conflicts manually. pnpm provides pnpm.peerDependencyRules for granular control: ignoreMissing to skip missing peer deps, allowedVersions to specify allowed version ranges, and allowAny to permit any version. pnpm also supports the overrides field for forcing dependency versions. This design makes pnpm's dependency tree stricter and more predictable.