Home/Development/How to Fix npm ERESOLVE unable to resolve dependency tree by Cause
Developmentnpm ERESOLVE unable to resolve dependency tree

How to Fix npm ERESOLVE unable to resolve dependency tree by Cause

A practical, cause-by-cause guide to the npm ERR! ERESOLVE unable to resolve dependency tree error. Compares --legacy-peer-deps, --force, and overrides, and covers npm ci failures in CI plus resetting package-lock.json.

How to Fix npm ERESOLVE unable to resolve dependency tree by Cause
Contents
  1. That error you saw—it’s one of these, right?
  2. What ERESOLVE actually is: the peer dependency policy change from npm v7
  3. Comparison of 4 fix recipes
  4. When npm ci breaks in CI / Docker
  5. Procedure to reset a tangled package-lock.json
  6. How yarn and pnpm differ
  7. Conclusion: a decision checklist by situation
  8. References: official docs
  9. FAQ

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.

Bash
# The fastest one-liner to unblock a stuck install
npm install --legacy-peer-deps

If 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.

TEXT
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 resolve

The 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 textMeaningNext step
npm ERR! code ERESOLVE / ERESOLVE unable to resolve dependency treenpm 7+ couldn't build a tree that includes peer dependencies. The header line of every conflictFind the package name in the lines below
Could not resolve dependency: peer react@"^17.0.0" from some-lib@x.y.zsome-lib requires react 17, but the project has a different majorNote the package and range, then pick a recipe
Conflicting peer dependency: ...Two packages require peer ranges that can't both be satisfiedUpgrade one side; if that's impossible, use overrides
npm WARN ERESOLVE overriding peer dependencyA warning, not an error. npm keeps a version outside the peer range and continues installingInstall finishes, but test that the library actually works
Fix the upstream dependency conflict, or retry this command with --force or --legacy-peer-depsnpm's workaround hintCheck 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 occurred
  • Found: react@18.2.0 → the version already locked in the current tree
  • Could 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.

ApproachCommand / configBehaviorSide effectsWhen to use
--legacy-peer-depsnpm install --legacy-peer-depsSkip peer checks the npm v6 wayPotential runtime compatibility riskUrgent build recovery; fine in most cases
--forcenpm install --forceIgnore conflicts and force the installLeaves a broken tree as-isLast resort
overridesJSON config in package.jsonPin a specific nested dependency versionRuntime errors if you pin the wrong versionSurgical fix; recommended for teams
Version alignmentMatch versions yourselfRoot-cause fix that satisfies peer requirementsTakes time; may require code changesPermanent fix

1) --legacy-peer-deps (the most reasonable temporary escape hatch)

Bash
npm install --legacy-peer-deps

If you don’t want to type it every time, put it in .npmrc at the project root.

INI
# .npmrc
legacy-peer-deps=true

2) --force (only when you are truly desperate)

Bash
npm install --force

It 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.

JSON
{
  "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.

Bash
npm ls react         # who uses react in the tree, and at which version
npm explain react    # trace back why a specific package was installed
TEXT
my-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-lib

The 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.

YAML
# 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 ci

Committing .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.

Dockerfile
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.

JSON
{ "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.

Bash
rm -rf node_modules package-lock.json
npm cache verify
npm install

Resetting 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.

Tasknpmyarnpnpm
Force a versionoverrides (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 mismatch7+ 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-depsresolutions after confirming real compatibilitypeerDependencyRules.allowedVersions / ignoreMissing
Reinstallrm -rf node_modules package-lock.json && npm installrm -rf node_modules yarn.lock && yarnrm -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.

  1. You only need the build unblocked right now → npm install --legacy-peer-deps
  2. You have identified the offending package → pin it with package.json overrides
  3. It only breaks in CI → unify the npm version (engines / .nvmrc) and commit .npmrc
  4. The lock is completely tangled → clean reset (PR required if the team shares it)
  5. 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.

Verification
✦ ✦ ✦
Sources and verification

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.

Published · ·Updated ·
Topic-related official docs (auto-linked · not per-claim sources)Node.js documentation ↗

Comments

Be the first to comment.