Fix npm ERESOLVE in 5 Minutes: Cause-by-Cause Fixes for unable to resolve dependency tree
If npm install was working fine yesterday and this morning it suddenly dumped a red error and stopped—and you copied that console output, searched, and landed here—you’re in the right place. This article has one goal: unblock the build in five minutes, then track down the root cause. For anyone in a hurry, here is the fastest temporary escape hatch first.
# The fastest one-liner to unblock a stuck install
npm install --legacy-peer-depsIf this command gets the build through, take a breath, then read on for why this happens and for cleaner long-term fixes.
That error you saw—it’s one of these, right?
For search accuracy, here is the actual console output. If you’ve seen any of the variants below, this article is exactly for you.
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: my-app@1.0.0
npm ERR! Found: react@18.2.0
npm ERR! node_modules/react
npm ERR! react@"^18.2.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^17.0.0" from some-legacy-lib@2.3.1
npm ERR! node_modules/some-legacy-lib
npm WARN ERESOLVE overriding peer dependency
npm error ERESOLVE could not resolveThe key phrases are ERESOLVE unable to resolve dependency tree, Could not resolve dependency: peer, and overriding peer dependency. The message is in English, which can be intimidating, but once you know the structure you can pinpoint the cause in five seconds.
| Console text | Meaning | Next step |
|---|---|---|
npm ERR! code ERESOLVE / ERESOLVE unable to resolve dependency tree | npm 7+ couldn't build a tree that includes peer dependencies. The header line of every conflict | Find the package name in the lines below |
Could not resolve dependency: peer react@"^17.0.0" from some-lib@x.y.z | some-lib requires react 17, but the project has a different major | Note the package and range, then pick a recipe |
Conflicting peer dependency: ... | Two packages require peer ranges that can't both be satisfied | Upgrade one side; if that's impossible, use overrides |
npm WARN ERESOLVE overriding peer dependency | A warning, not an error. npm keeps a version outside the peer range and continues installing | Install finishes, but test that the library actually works |
Fix the upstream dependency conflict, or retry this command with --force or --legacy-peer-deps | npm's workaround hint | Check the risk in the comparison table below before choosing |
What ERESOLVE actually is: the peer dependency policy change from npm v7
The root of this error is a policy change in npm. Through npm v6, peer dependency conflicts were simply ignored (auto-flattened): npm printed a warning and continued the install. From npm v7 onward, npm tries to install peer dependencies automatically, and if the dependency tree cannot satisfy everything at once (e.g. A requires react 17 while B requires react 18), it aborts the install entirely (ERESOLVE). With npm v10 as the default today, this strict behavior is the standard, and the error has exploded especially after the React 19 release because older libraries have not yet updated their peer ranges. Reading the log line by line:
While resolving: my-app@1.0.0→ which package was being installed when the conflict occurredFound: react@18.2.0→ the version already locked in the current treeCould not resolve dependency: peer react@"^17.0.0" from some-legacy-lib→ which package wants what, and failed to match
In other words, the log means: “My project uses react 18, but some-legacy-lib insists on react 17 only.”
Comparison of 4 fix recipes
Pick the prescription that matches your situation. Higher in the table is faster and rougher; lower is slower but more fundamental.
| Approach | Command / config | Behavior | Side effects | When to use |
|---|---|---|---|---|
--legacy-peer-deps | npm install --legacy-peer-deps | Skip peer checks the npm v6 way | Potential runtime compatibility risk | Urgent build recovery; fine in most cases |
--force | npm install --force | Ignore conflicts and force the install | Leaves a broken tree as-is | Last resort |
overrides | JSON config in package.json | Pin a specific nested dependency version | Runtime errors if you pin the wrong version | Surgical fix; recommended for teams |
| Version alignment | Match versions yourself | Root-cause fix that satisfies peer requirements | Takes time; may require code changes | Permanent fix |
1) --legacy-peer-deps (the most reasonable temporary escape hatch)
npm install --legacy-peer-depsIf you don’t want to type it every time, put it in .npmrc at the project root.
# .npmrc
legacy-peer-deps=true2) --force (only when you are truly desperate)
npm install --forceIt carries a broken tree forward, so it can cause nastier runtime bugs. Avoid it unless you are about to demo.
3) package.json overrides (the approach I prefer most)
In practice I prefer pinning just the offending package with overrides rather than ignoring peer checks globally with --legacy-peer-deps. The change lives in code, so teammates can trace what was overridden and how.
{
"overrides": {
"some-legacy-lib": {
"react": "$react"
},
"another-lib": {
"react-dom": "18.2.0"
}
}
}"$react" means “follow whatever react version the root project uses.” overrides works on npm 8.3 and later. A package listed directly in the root package.json dependencies can't be overridden with a different spec; that throws an EOVERRIDE error. So instead of writing a version like "react": "18.3.1", it's safer to reference the root spec with "$react". After applying it, re-lock with rm -rf node_modules package-lock.json && npm install.
4) Version alignment (the root-cause fix)
Use a command like npm ls react to see who requires which version, then upgrade or replace the conflicting package with a version that supports the peer. It takes time, but it is the proper way to get a clean lock file.
npm ls react # who uses react in the tree, and at which version
npm explain react # trace back why a specific package was installedmy-app@1.0.0
├── react@18.3.1
└─┬ some-legacy-lib@2.3.1
└── react@18.3.1 deduped invalid: "^17.0.0" from node_modules/some-legacy-libThe line marked invalid is where the peer range isn't satisfied.
When npm ci breaks in CI / Docker
The most common case is “works locally, fails only in CI.” Unlike npm install, npm ci requires the lock file and package.json to match 100%, and it fails immediately on mismatch or a peer conflict.
On GitHub Actions there are two approaches.
# Approach A: pass the flag on the step
- run: npm ci --legacy-peer-deps
# Approach B: commit .npmrc (legacy-peer-deps=true) → the step is just npm ciCommitting .npmrc makes local, CI, and Docker behave the same, which is better for consistency. In a Dockerfile, setting it via an environment variable keeps cache layers intact and stays clean.
ENV NPM_CONFIG_LEGACY_PEER_DEPS=true
COPY package*.json ./
RUN npm ci
COPY . .The trap you hit most often is an npm version mismatch between local and CI. If local is npm v9 and CI is v10, resolve results differ and you get the “it worked on my machine” incident. Pin versions with engines in package.json and .nvmrc.
{ "engines": { "node": ">=20", "npm": ">=10" } }Procedure to reset a tangled package-lock.json
This is the clean-reset sequence when the lock file is partially corrupted and no command works.
rm -rf node_modules package-lock.json
npm cache verify
npm installResetting the lock is a double-edged sword. Every dependency is re-locked to the latest compatible version, so you can lose reproducibility. If you reset a team-shared lock file alone and commit it, you can break a pile of “it was fine a minute ago” teammate environments—always share the change in a PR and get a review. Also, pnpm and Yarn Berry use a strict peer policy by default, so if you are considering a migration, expect these conflicts more often. In a monorepo (workspaces), check hoisting locations as well.
For reference, the Python world has the same pain. In pip the equivalent dependency conflict shows up as ResolutionImpossible; the philosophy is similar but the tools are completely different (related: pip ResolutionImpossible troubleshooting guide).
How yarn and pnpm differ
The same peer conflict behaves differently by package manager, and the settings live in different places.
| Task | npm | yarn | pnpm |
|---|---|---|---|
| Force a version | overrides (package.json, 8.3+) | resolutions (package.json) | overrides (pnpm-workspace.yaml on pnpm 10+; pnpm.overrides in package.json on older versions) |
| Default on peer mismatch | 7+ fails the install (ERESOLVE) | Prints a warning and continues (both Classic and Berry) | Default strictPeerDependencies: false, so it only warns; fails only if you set it to true |
| Silence/work around warnings | --legacy-peer-deps | resolutions after confirming real compatibility | peerDependencyRules.allowedVersions / ignoreMissing |
| Reinstall | rm -rf node_modules package-lock.json && npm install | rm -rf node_modules yarn.lock && yarn | rm -rf node_modules pnpm-lock.yaml && pnpm install |
An install that passes in yarn or pnpm doesn't mean the peer problem is gone. Test the packages named in the warnings.
Conclusion: a decision checklist by situation
Apply these in urgency order, but do not stay on the workaround—follow the roadmap.
- You only need the build unblocked right now →
npm install --legacy-peer-deps - You have identified the offending package → pin it with
package.jsonoverrides - It only breaks in CI → unify the npm version (
engines/.nvmrc) and commit.npmrc - The lock is completely tangled → clean reset (PR required if the team shares it)
- You have time → align conflicting package versions for a root-cause fix
--legacy-peer-deps is first aid, not a cure. If a deadline forced you onto a workaround, leave a single “dependency cleanup” ticket in the backlog. It will save you on the next React major upgrade.
References: official docs
The primary sources for the behavior, settings, and errors covered in this article are the following official docs. Check them for version-specific options and exact behavior.
FAQ
Q. What is the difference between --legacy-peer-deps and --force?
A. --legacy-peer-deps skips only peer dependency validation and still builds the rest of the tree normally. --force ignores conflicts and forcibly installs even a broken tree, so it is much more dangerous. Use --legacy-peer-deps day to day; save --force for a true last resort.
Q. It works locally, but CI npm ci fails with ERESOLVE.
A. Usually an npm version mismatch between local and CI, or a lock file mismatch. Pin Node/npm with .nvmrc and engines, and commit legacy-peer-deps=true in .npmrc so environments match.
Q. Is it safe at runtime if I force-pin with overrides?
A. It is safe when the library actually works with the newer version and only the peer range was never updated. If you force-fit a truly incompatible major difference, you can get runtime errors—always run core feature tests after pinning.
Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.