npm ERESOLVE Fix 7 min read

npm ERR! ERESOLVE Could Not Resolve Dependency Tree

Fix npm ERR! ERESOLVE unable to resolve dependency tree — read the conflict, pick the right escape hatch, and apply a fix that does not leave a broken node_modules behind.

Diagnosis and fixes

1. Why npm 7+ suddenly started blocking installs

Before npm 7, peer dependency mismatches were just a console warning — the install continued anyway. npm 7 changed two things at once: it started auto-installing peer dependencies (so you no longer had to npm install them yourself), and it started treating an unresolvable peer conflict as a hard error instead of a warning. That is why a package.json that installed fine on npm 6 now fails on npm 7–10.

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
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@"^16.8.0 || ^17.0.0" from [email protected]
npm ERR! node_modules/react-beautiful-dnd
npm ERR!   react-beautiful-dnd@"^13.1.1" from the root project

2. Read the tree to find the actual conflict

The error above always has two halves. Found: tells you what version is currently in your tree and why it's there. Could not resolve dependency: peer X from Y tells you which package (Y) declares a peer range that the found version does not satisfy. In the example above: your root project wants React 18, but [email protected] only accepts React 16 or 17 as a peer. Those two lines are the entire diagnosis — skip straight to them, ignore the rest of the tree noise.

  • Common real-world pair: React 18 vs a UI library still pinned to a React 17 peer range (react-beautiful-dnd, older react-select, older framer-motion).
  • Another common pair: ESLint 9 vs a plugin whose peerDependencies still says "eslint": "^8.0.0" because the plugin has not shipped a flat-config / ESLint 9 compatible release yet.

3. The correct fix: upgrade or override, not flags

Option A — upgrade the conflicting package. Check whether a newer major version already supports your peer's range:

npm view react-beautiful-dnd peerDependencies
npm view react-beautiful-dnd versions --json

# If a compatible major exists:
npm install react-beautiful-dnd@latest

Option B — npm overrides. If the package is abandoned or you can't upgrade yet, but you've verified it actually works with the newer peer at runtime, force the version in package.json:

{
  "name": "my-app",
  "overrides": {
    "react-beautiful-dnd": {
      "react": "$react",
      "react-dom": "$react-dom"
    }
  }
}

The $react syntax reuses whatever version is already at the root, so it stays in sync automatically. For pnpm, the equivalent lives under pnpm.overrides instead:

{
  "pnpm": {
    "overrides": {
      "react-beautiful-dnd>react": "$react"
    }
  }
}

After editing overrides, delete the lockfile and node_modules and reinstall clean — overrides only apply cleanly against a fresh resolution:

rm -rf node_modules package-lock.json
npm install

4. The escape hatches: --legacy-peer-deps vs --force

Both flags get you unblocked; neither one fixes the underlying mismatch. Use them to keep moving, then come back and apply option A or B above.

# Skips peer dependency checking entirely (npm 6 behavior)
npm install --legacy-peer-deps

# Checks peers, reports the conflict, installs anyway;
# also re-fetches and overwrites cached packages
npm install --force
When --legacy-peer-deps is actually safe: when you've manually confirmed the conflicting package doesn't touch any API surface that changed between the two peer major versions — e.g. a small CSS-in-JS utility whose React usage is trivial. It is not safe to reach for by default on core UI libraries like state managers, routers, or drag-and-drop libs, where major version peer bumps usually track real breaking API changes.

5. npm vs yarn vs pnpm strictness

The three package managers disagree on how strictly to enforce peer ranges, because they structure node_modules differently:

  • pnpm (strictest): non-flat, symlinked structure with no hoisting by default. A peer dependency must genuinely resolve within the dependent's own graph, so mismatches are impossible to hide. pnpm also warns on unmet peers by default even when the install technically succeeds.
  • npm 7+ (strict, blocking): flat hoisted tree, but ERESOLVE hard-fails on unresolvable conflicts unless you pass --legacy-peer-deps or --force.
  • yarn classic / yarn berry (looser): yarn classic only warns on peer mismatches, similar to old npm 6 behavior. Yarn Berry with PnP is stricter again, closer to pnpm, but plug'n'play mode is opt-in.
🔔

Prismix tracks npm registry status alongside your other AI and dev services

Free status monitoring with email alerts — no credit card required.

FAQ

What does npm ERR! ERESOLVE could not resolve mean?

Two packages in your tree require conflicting versions of the same peer dependency and npm cannot pick one automatically. Since npm 7, this fails the install instead of just printing a warning.

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

It's safe as a temporary unblock, not a permanent fix. It skips peer checks entirely, so it can install a combination that's actually incompatible at runtime. Follow up with an upgrade or an overrides entry.

What's the difference between --legacy-peer-deps and --force?

--legacy-peer-deps disables peer checking entirely. --force still detects conflicts but installs anyway and re-downloads cached packages — more aggressive and more likely to leave a broken tree.

Why is pnpm stricter about peer dependencies than npm?

pnpm's symlinked, non-hoisted node_modules means a peer must genuinely resolve within the dependent's own graph. npm and yarn's flat hoisted structure can accidentally satisfy a peer with whatever happens to be hoisted, hiding real mismatches.

Monitor related services