npm ERR! code ERESOLVE — unable to resolve dependency tree

Quick answer

ERESOLVE means a package's peer dependency conflicts with a version already in your tree, and npm 7+ refuses to install a broken tree. The real fix is to align the versions; the fast workaround is npm install --legacy-peer-deps.

The exact error string

A typical ERESOLVE failure from npm install looks like this:

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 react-some-widget@2.1.0
npm ERR! node_modules/react-some-widget
npm ERR!   react-some-widget@"^2.1.0" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.

Read it bottom-up. The Could not resolve dependency block is the key: react-some-widget@2.1.0 declares a peer of react@"^17.0.0", but the root project has react@18.2.0 installed. 18 does not satisfy ^17, so npm cannot build one tree that makes both happy.

What is a peer dependency?

A peer dependency is a package that a library expects you to provide, rather than bundling its own copy. Plugins do this constantly: a React component library declares react as a peer so it shares the single React instance in your app instead of shipping a second one. The library says “I work with React 17,” and it is your job to have a compatible React installed.

The trouble starts when two packages disagree about which version you should provide. One peer wants React 17, your app uses React 18, and there is no single version that satisfies both ranges. That irreconcilable overlap is exactly what ERESOLVE reports.

Why npm 7+ throws this when npm 6 did not

This error is new to most people because npm changed its behavior. npm 6 quietly ignored peer dependency conflicts — it would print a warning at most and install whatever it could, leaving you with a tree the package authors never tested. npm 7 (bundled with Node 16) began treating unmet peer dependencies as hard errors and stops with ERESOLVE rather than installing a tree it considers incorrect.

The change was deliberate. npm 7 introduced strict peer dependency resolution specifically to prevent broken dependency trees — the kind that installed cleanly under npm 6 but then failed at runtime with cryptic symptoms like Cannot find module, duplicate-React bugs, or mismatched type definitions. Surfacing the conflict at install time, when it is cheap to fix, is the entire point: a failed install is far better than a green install that crashes in production.

npm 8, 9, and 10 — shipped with Node 18, 20, and 22 — all keep this strict behavior. So if the error appeared right after you upgraded Node or switched machines, the conflict was almost certainly there all along; your older npm simply never told you about it.

Fix 1: Align the versions (the correct fix)

The only fix that yields a genuinely correct tree is to make the versions compatible. Read the error to see which range is unmet, then resolve it one of three ways:

# See if a newer version supports your React
npm view react-some-widget peerDependencies
npm view react-some-widget versions --json

# Upgrade to a version whose peer range matches React 18
npm install react-some-widget@latest

Fix 2: --legacy-peer-deps (the common workaround)

When the dependency simply has not updated its peer range yet — but you know it works with your version — tell npm to install the way npm 6 did and skip the peer check entirely:

npm install --legacy-peer-deps

# Make it the default for this project (local + CI behave the same):
# create or edit .npmrc in the project root
echo "legacy-peer-deps=true" >> .npmrc

This is the right tool when a maintainer is slow to widen a peer range that is, in practice, compatible. The trade-off: you are installing a combination of versions nobody officially tested, so verify your app still runs. If you add it to .npmrc, leave a comment explaining why — treat it as temporary, not a permanent silencer.

Fix 3: overrides for buried transitive conflicts

Sometimes the conflict is not in a package you installed directly, but deep in a sub-dependency you do not control. npm 8.3+ lets you force a resolved version with an overrides block in package.json:

{
  "overrides": {
    "react-some-widget": {
      "react": "$react"
    },
    "some-deep-dep": "1.4.0"
  }
}

The "react": "$react" syntax pins the nested peer to whatever version your root project uses. Use overrides surgically — you are overruling what a package author declared, so confirm the forced version actually works. (Yarn calls this resolutions; pnpm uses pnpm.overrides.)

Fix 4: --force (last resort)

npm install --force overrides all conflicts, not just peer ones, and will happily install a duplicated or mismatched tree. It is broader and riskier than --legacy-peer-deps and can produce a node_modules that is genuinely broken at runtime. Reach for it only when nothing else works and you are prepared to debug the result.

# Targeted: ignore ONLY peer dependency conflicts (prefer this)
npm install --legacy-peer-deps

# Heavy-handed: override ALL conflicts (can break your tree)
npm install --force

Which fix should I use? (decision flow)

Work top-down. Each step is one question; if the answer is yes, take the green action and stop. If no, drop to the next question. The last resort — --legacy-peer-deps — is only for when nothing above it is possible.

npm ERESOLVE decision flow Step 1: if you can upgrade the conflicting package, do that (Fix 1). Otherwise step 2: if you can downgrade your framework, do that (Fix 1). Otherwise step 3: if the conflict is in a buried sub-dependency, add an overrides block (Fix 3). Otherwise, as a last resort, install with --legacy-peer-deps (Fix 2). npm ERESOLVE 1. Can you upgrade the conflicting package to a version that supports your framework? ✓ Yes → upgrade the package (Fix 1). Done. no 2. Can you downgrade your framework (e.g. React) to satisfy the peer range? ✓ Yes → downgrade the framework (Fix 1). Done. no 3. Is the conflict in a buried sub-dependency you do not directly control? ✓ Yes → add an overrides block to pin the version (Fix 3). Done. still blocked 4. Last resort: install with --legacy-peer-deps (Fix 2) and verify the app runs. Treat it as temporary.

Common scenarios

Most ERESOLVE errors come from a handful of framework upgrades. If your conflict matches one of these, jump straight to the recommended action:

What you haveWhat the package wantsRecommended fix
React 19 peer react@^18 Upgrade the package to a React 19–compatible release; if none exists yet, --legacy-peer-deps until the author updates
React 18 peer react@^17 Upgrade the package, or pin react with an overrides block if it works in practice
Next.js (mismatched React) Next pins its own React range Upgrade Next.js — it controls the supported React version; do not fight it by downgrading React
Angular 17 peer @angular/*@^16 Install the library version built for Angular 17 (Angular libraries are tightly version-locked)
ESLint 9 peer eslint@^8 Upgrade the plugin to its ESLint 9 (flat-config) release; many plugins shipped one
TypeScript 5 peer typescript@^4 Upgrade the types/plugin package; if it genuinely works on TS 5, --legacy-peer-deps

ERESOLVE in CI but not locally

A frequent variant: npm install works on your laptop but CI fails with ERESOLVE. The usual cause is a stale package-lock.json — your local node_modules was built before the conflict, while CI installs from scratch. If CI uses npm ci (which it should), it installs strictly from the lockfile and surfaces the conflict immediately.

The fix is to resolve the conflict locally, delete node_modules and package-lock.json, run a fresh npm install, and commit the regenerated lockfile. Never “fix” CI by hand-editing the lockfile — regenerate it from a clean install so the resolution is reproducible.

Debugging checklist

Frequently Asked Questions

What does npm ERR! code ERESOLVE mean?

ERESOLVE means npm could not build a single dependency tree that satisfies every package's requirements at once. Almost always, one package declares a peer dependency on a version of another package that conflicts with the version your project (or another dependency) has installed. npm 7 and later refuse to install a tree they consider broken, so they stop with ERESOLVE instead of guessing.

Is --legacy-peer-deps safe to use?

It is usually safe and is the most common workaround. --legacy-peer-deps tells npm to ignore peer dependency conflicts and install the tree the way npm 6 did. The risk is that you may install a combination of versions the package authors never tested together, which can cause subtle runtime bugs. Prefer fixing the actual version mismatch; use --legacy-peer-deps when a dependency simply has not updated its peer range yet.

What is the difference between --legacy-peer-deps and --force?

--legacy-peer-deps ignores peer dependencies entirely, behaving like npm 6 and skipping the conflict check. --force is broader and more dangerous: it overrides all conflicts, can install duplicate or mismatched versions, and may produce a genuinely broken node_modules. For ERESOLVE, --legacy-peer-deps is the targeted option; reach for --force only as a last resort.

How do I permanently fix ERESOLVE instead of using a flag every time?

Fix the version mismatch in package.json. Read the error to see which peer range is unmet, then either upgrade the conflicting package to a version whose peer range matches, downgrade the package that is too new, or add an overrides block (npm 8.3+) to force a specific transitive version. Once package.json and package-lock.json resolve cleanly, you no longer need any install flag.

Why did this start happening after I upgraded npm or Node?

npm 6 silently ignored peer dependency conflicts; npm 7 (shipped with Node 16) began enforcing them and fails with ERESOLVE. Node 18, 20, and later bundle npm 8/9/10, which keep that strict behavior. The conflict was almost certainly present before — your old npm just installed a broken tree without telling you.

Should I set legacy-peer-deps in .npmrc for CI?

You can add legacy-peer-deps=true to a project .npmrc so local installs and CI behave identically, which avoids “works on my machine” failures. Treat it as a temporary measure and document why it is there. The cleaner long-term fix is to resolve the peer conflict so the install succeeds without any override at all.

Working with a JSON API?

Format and validate any JSON response in your browser — nothing is uploaded to a server.

JSON Formatter JSON Validator All Error References
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.