How Do I Resolve npm and Yarn Dependency Conflicts?

Type: Software Reference Confidence: 0.93 Sources: 7 Verified: 2026-02-20 Freshness: evolving

TL;DR

Constraints

Quick Reference

# Error / Symptom Likelihood Diagnostic Command Fix
1 ERESOLVE unable to resolve dependency tree ~45% npm install 2>&1 | head -50 Read conflict, align versions or add overrides [src6]
2 Could not resolve dependency: peer X@^Y from Z ~25% npm explain <package> Upgrade Z to version supporting current X [src7]
3 npm WARN ERESOLVE overriding peer dependency ~10% npm ls <package> Review if override is intentional; may need overrides [src1]
4 Invalid peer dependency after --legacy-peer-deps ~8% npx check-peer-dependencies Remove flag, fix root cause with version alignment [src6]
5 Yarn warning ... has unmet peer dependency ~5% yarn why <package> Add resolutions to package.json [src3]
6 pnpm WARN ... requires peer but none installed ~4% pnpm why <package> Install missing peer or use pnpm.peerDependencyRules [src4]
7 Duplicate packages in bundle ~3% npm dedupe --dry-run Run npm dedupe or add overrides [src1]
8 npm ci fails but npm install works Common Check lockfile freshness Regenerate: rm package-lock.json && npm install [src6]

Decision Tree

START — npm/yarn install fails with dependency conflict
├── Which package manager?
│   ├── npm 7+
│   │   ├── Error says "ERESOLVE unable to resolve dependency tree"?
│   │   │   ├── YES → Run `npm explain <conflicting-package>` [src6]
│   │   │   │   ├── Can you upgrade the parent package?
│   │   │   │   │   ├── YES → `npm install <parent>@latest`
│   │   │   │   │   └── NO → Add `overrides` to package.json [src1, src5]
│   │   │   │   └── Is this a dev-only tool (linter, test runner)?
│   │   │   │       ├── YES → `--legacy-peer-deps` is acceptable [src6]
│   │   │   │       └── NO → Fix with overrides
│   │   │   └── NO → Check `npm ls` for version conflicts
│   │   └── Error says "peer dep ... from ..."?
│   │       └── `npm explain <package>` → upgrade or override [src7]
│   ├── Yarn Classic (v1) or Yarn Berry (v2+)
│   │   └── Add `resolutions` to root package.json → `yarn install` [src3]
│   └── pnpm
│       └── Install missing peer or configure peerDependencyRules [src4]
└── Still failing?
    └── Nuclear: rm -rf node_modules package-lock.json && npm cache clean --force && npm install [src6]

Step-by-Step Guide

1. Identify the conflicting packages

Run npm install and read the ERESOLVE output carefully. It tells you exactly which packages conflict. [src6]

# See the full error (npm truncates by default)
npm install 2>&1 | head -80

# Or use verbose mode for maximum detail
npm install --verbose 2>&1 | grep -A 5 "ERESOLVE"

Verify: The error shows While resolving: [email protected] and Could not resolve dependency: peer react@"^17.0.0" from [email protected].

2. Understand why each version is required

Use npm explain (alias: npm why) to trace the dependency chain. [src5, src6]

# Show which packages depend on react and what versions they require
npm explain react

# Tree view of all installed versions
npm ls react

# Check for all outdated packages
npm outdated

Verify: npm explain react shows each dependant with its required version range.

3. Attempt version alignment (preferred fix)

Update the package that has the outdated peer dependency requirement. [src7]

# Update a specific package to its latest version
npm install @testing-library/react@latest

# Update all packages to latest compatible versions
npm update

# Check what would change without modifying anything
npm update --dry-run

Verify: npm ls react shows a single version with no UNMET PEER DEPENDENCY warnings.

4. Use overrides for npm (when alignment is impossible)

When a transitive dependency pins an incompatible version and no update is available. [src1, src5]

{
  "dependencies": {
    "some-library": "^2.0.0"
  },
  "overrides": {
    "react": "^18.2.0",
    "some-library": {
      "old-subdep": "2.0.0"
    }
  }
}
# After adding overrides, always clean install
rm -rf node_modules package-lock.json
npm install

# Verify the override took effect
npm ls old-subdep
npm explain old-subdep

Verify: npm ls old-subdep shows only the overridden version.

5. Use resolutions for Yarn

Yarn Classic and Berry both support resolutions in package.json. [src3]

{
  "dependencies": {
    "some-library": "^2.0.0"
  },
  "resolutions": {
    "react": "18.2.0",
    "**/lodash": "4.17.21",
    "some-library/old-subdep": "2.0.0"
  }
}
# Clean install after changing resolutions
rm -rf node_modules yarn.lock
yarn install

# Verify
yarn why react

6. Configure pnpm peer dependency rules

pnpm provides granular control via pnpm.peerDependencyRules in package.json. [src4]

{
  "pnpm": {
    "peerDependencyRules": {
      "ignoreMissing": ["@babel/core"],
      "allowedVersions": {
        "react": "18"
      },
      "allowAny": ["eslint"]
    },
    "overrides": {
      "lodash": "4.17.21"
    }
  }
}
# Clean install
rm -rf node_modules pnpm-lock.yaml
pnpm install

# Verify
pnpm why react

Code Examples

package.json: npm overrides for React peer conflict

// Input:  Library requires react@^17 but project uses react@18
// Output: Forces all packages to accept [email protected]

{
  "name": "my-app",
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "legacy-component-lib": "^3.0.0"
  },
  "overrides": {
    "react": "$react",
    "react-dom": "$react-dom"
  }
}
// Note: "$react" references the version in your own dependencies.
// Requires npm >= 8.3.0. [src1, src2]

.npmrc: project-level configuration for persistent flags

# Input:  Project that must use --legacy-peer-deps on every install
# Output: Flag applied automatically without passing it each time

# .npmrc (place in project root, commit to git)
legacy-peer-deps=true

# Prefer deduplication when resolving (npm 8.13+)
prefer-dedupe=true

# Strict engine checks
engine-strict=true

Bash: automated dependency conflict diagnosis

#!/bin/bash
# Input:  Run in project root with package.json
# Output: Conflict report with fix suggestions

echo "=== npm Dependency Conflict Diagnosis ==="
echo "npm version: $(npm --version)"
echo "node version: $(node --version)"
echo ""

echo "--- Peer dependency check ---"
npx check-peer-dependencies 2>/dev/null || echo "(install: npm i -g check-peer-dependencies)"
echo ""

echo "--- Duplicate packages ---"
npm ls --all 2>&1 | grep "deduped" | sort | uniq -c | sort -rn | head -10
echo ""

echo "--- Outdated packages ---"
npm outdated --long 2>/dev/null | head -20
echo ""

echo "--- Install dry run (conflicts) ---"
npm install --dry-run 2>&1 | grep -E "(ERESOLVE|peer|conflict|WARN)" | head -20

Anti-Patterns

Wrong: Using --force or --legacy-peer-deps as default

# BAD — hides real incompatibilities that cause runtime errors [src6, src7]
npm install --force
# or
npm config set legacy-peer-deps true  # global — affects ALL projects!
# Silences the error without fixing the conflict.

Correct: Diagnose first, then fix the root cause

# GOOD — understand the conflict, then fix it [src5, src6]
npm explain react            # see why each version is needed
npm install some-lib@latest  # update the conflicting package
# If no update available: add "overrides" to package.json

Wrong: Deleting package-lock.json on every conflict

# BAD — loses reproducibility, may introduce NEW conflicts [src6]
rm package-lock.json
npm install
# Different versions resolved every time. CI becomes non-deterministic.

Correct: Targeted lockfile regeneration

# GOOD — only regenerate when you've actually changed overrides/versions
rm -rf node_modules package-lock.json
npm install
git add package-lock.json && git commit -m "Regenerate lockfile after override change"

Wrong: Wildcard overrides without version constraint

// BAD — "*" means any version, including breaking majors [src1, src5]
{
  "overrides": {
    "lodash": "*"
  }
}

Correct: Pin to specific version or range

// GOOD — explicit version, predictable behavior [src1, src5]
{
  "overrides": {
    "lodash": "4.17.21"
  }
}

Common Pitfalls

Diagnostic Commands

# === Identify conflicts ===
npm ls --all 2>&1 | head -100          # full dependency tree
npm explain <package-name>              # trace dependency chain (alias: npm why)
npm outdated --long                     # list all outdated packages
npm install --dry-run 2>&1 | grep -E "ERESOLVE|peer|conflict"

# === Yarn equivalents ===
yarn why <package-name>
yarn list --pattern <package-name>

# === pnpm equivalents ===
pnpm why <package-name>
pnpm ls <package-name>

# === Fix commands ===
npm dedupe                              # reduce duplicate versions
npm dedupe --dry-run                    # preview changes
rm -rf node_modules package-lock.json   # clean slate
npm cache clean --force                 # clear npm cache
npm install                             # fresh install
npx check-peer-dependencies             # verify peer deps satisfied

# === Version checks ===
npm --version                           # need >= 8.3 for overrides
node --version                          # check engine compatibility

Version History & Compatibility

Version / Tool Change Impact
npm 6.x Peer deps NOT auto-installed, warnings only No ERESOLVE errors; conflicts were silent [src6]
npm 7.0 (Oct 2020) Peer deps auto-installed, ERESOLVE introduced Breaking — many projects hit new errors [src6]
npm 8.3 (Dec 2021) overrides field added Proper fix for transitive dependency conflicts [src1, src2]
npm 8.13 (Jun 2022) --prefer-dedupe flag added Reduces duplicate versions during install
npm 9.x (Oct 2022) Lockfile v3, stricter resolution Better conflict detection
npm 10.x (Sep 2023) Current stable, ships with Node 20+ overrides fully stable, improved error messages
Yarn Classic 1.x resolutions always supported Simple version pinning [src3]
Yarn Berry 2-4 resolutions + strict peer mode Stricter than npm; Plug'n'Play changes resolution [src3]
pnpm 7+ Strict peer deps, peerDependencyRules Does not auto-install conflicting peers [src4]
pnpm 9.x (2024) Current stable, enhanced overrides pnpm.overrides for version pinning [src4]

When to Use / When Not to Use

Use When Don't Use When Use Instead
npm install fails with ERESOLVE Package is fundamentally incompatible with your stack Switch to an alternative package
Need to pin a transitive dependency version You can upgrade the direct dependency to resolve the conflict npm update <package>
Security patch for nested dependency Override would change major version with breaking API Wait for upstream fix or fork
CI builds fail on peer dep warnings --legacy-peer-deps is already set globally Remove global flag, fix per-project
Monorepo with shared dependencies Each workspace needs a different version Use workspace-specific dependency ranges

Important Caveats

Related Units