BlueByte
ERESOLVEWorkaround

npm install fails with ERESOLVE peer dependency conflict

By Haneul SeoUpdated August 23, 20263 min

Hi, it's BlueByte. An ERESOLVE wall of text looks alarming, but it is not noise — it is npm pointing at exactly two packages that disagree about a version. Let's read which two, fix it the right way when a compatible release exists, and use the escape hatch only when you are genuinely ahead of the ecosystem.

What the error is telling you

npm install stops with a tree it cannot resolve:

npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error Found: react@19.0.0
npm error Could not resolve dependency:
npm error peer react@"^18.0.0" from some-lib@3.2.0

Since npm 7, peer dependencies are installed and enforced. When a package demands a peer version that clashes with the tree, the install fails instead of silently continuing the way npm 6 did. Read the "Found" and "peer" lines together — they name the disagreement.

Why a peer requirement blocks the install

The error names the conflict exactly: a package (some-lib) declares a peer requirement (react@^18) that your installed version (react@19) does not satisfy. That is a real compatibility statement from the author, not an npm bug. It shows up most often right after a major release of a framework, before some libraries have widened their peer range — and also when two of your dependencies each pin an incompatible version of a shared peer, so no single version satisfies both.

Check whether a compatible release already exists

Read the tree npm printed to see which package wants which peer, then ask npm whether a newer version fits:

npm view some-lib peerDependencies
npm view some-lib versions --json

If a newer some-lib lists your framework version in its peer range, the fix is a version bump. If none does, you are ahead of the ecosystem and need the temporary escape hatch. When two of your own dependencies clash, npm ls react shows both and which versions they each expect.

The right fix, then the escape hatch

  1. Align versions — this is the real fix. Upgrade the offending package to one that supports your peer:
npm install some-lib@latest
  1. If no compatible version exists yet, install anyway and accept the risk knowingly:
npm install --legacy-peer-deps

This restores npm 6 behavior — peers are ignored during resolution. Treat it as a deliberate, temporary choice and retest the feature the package provides.

A real case: the day after a major release

The day after React 19 ships, npm install fails because some-chart-lib@3 declares peer react@^18. npm view some-chart-lib versions shows a 4.0.0 released hours ago, and npm view some-chart-lib@4 peerDependencies lists react@^18 || ^19. So the real fix is a bump: npm install some-chart-lib@4, and the tree resolves cleanly with no flags. Had no v4 existed, you would have used --legacy-peer-deps as a stopgap, retested the charts, and removed the flag once the maintainer widened the range.

Confirm it works, not just installs

Install cleanly and check nothing broke at runtime:

rm -rf node_modules package-lock.json && npm install
npm test

A clean install with passing tests means the versions actually work together — not just that npm stopped complaining.

Keep it from recurring

Keep dependencies moving in step rather than bumping one major far ahead of the rest, and record any --legacy-peer-deps decision in a comment or .npmrc with the reason, so the next person knows it is intentional and can remove it once the ecosystem catches up.

How this differs from --force and from a 404

--legacy-peer-deps ignores peer conflicts; --force does that and more and can install a genuinely broken tree, so prefer the former. And a 404 or ETARGET is a different failure — the package or version does not exist — not a peer conflict. When you see ERESOLVE, look for the two disagreeing versions first; the fix is almost always to reconcile them.

Related questions

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

--legacy-peer-deps ignores peer dependency conflicts during resolution. --force does that and more, and can produce a broken tree. Prefer --legacy-peer-deps.

Should I put legacy-peer-deps in .npmrc?

Only as a stopgap for one known conflict, with a note. Leaving it on permanently hides real incompatibilities in future installs.

It works with --legacy-peer-deps — am I done?

Only if the feature actually works at runtime. Run the app and its tests; the peer requirement exists for a reason, and ignoring it can surface as a runtime bug.

Does yarn or pnpm avoid this?

They handle peer conflicts differently and may warn rather than fail, but the underlying incompatibility is the same. Aligning versions is still the real fix.

Two of my dependencies want different versions of the same peer.

Run npm ls <peer> to see both. You need a version of one dependency whose peer range overlaps the other's; if none exists, one of them has to change.

References

Haneul Seo

Infrastructure engineer · 10+ years running Linux fleets

More in this category

detected dubious ownershipFixed

Git: fatal: detected dubious ownership in repository

Since the CVE-2022-24765 fix in Git 2.35.2, Git refuses to read a repository whose working tree or .git directory is owned by a different user than the one running the command. It shows up in containers, CI jobs, sudo sessions and shared drives. Fix the ownership if the repo should be yours, or add the exact path to safe.directory in your global config — never in the repo's own config, which Git ignores for this.

Git
failed calling webhookFixed

Kubernetes: Internal error occurred: failed calling webhook

An admission webhook sits in front of your write, the API server could not get an answer out of it, and failurePolicy: Fail turned that silence into a rejection. The tail of the message is the whole diagnosis: context deadline exceeded means the call went nowhere, no endpoints available means nothing is running, and an x509 line means the API server does not trust the webhook's certificate. Each has a different fix, and none of them is your manifest.

Kubernetes
1205Fixed

MySQL: ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

A statement waited the full innodb_lock_wait_timeout for a row lock another transaction is still holding, and gave up. sys.innodb_lock_waits names the blocking session and hands you the KILL statement, and a blocking_query of NULL means the blocker is idle on an open transaction. The detail most retry loops get wrong: by default only the timed-out statement is rolled back, so your transaction is still open and still holds every lock it took earlier.

MySQL
MISCONFFixed

Redis: MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk

Reads keep working and every write is rejected, because the last background save failed and stop-writes-on-bgsave-error defaults to yes. The log names the real cause — no space, a dir the redis user can't write, a read-only mount at rename time, or fork failing with Cannot allocate memory. Fix the cause, run one BGSAVE, and writes come back on their own with no restart: rdb_last_bgsave_status flips from err to ok. Setting stop-writes-on-bgsave-error no restores writes instantly but leaves the snapshot broken, so treat it as a deliberate trade, not the fix.

Redis
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memoryFixed

Node.js: FATAL ERROR: Reached heap limit — JavaScript heap out of memory (exit 134)

The V8 heap has its own ceiling, derived from system memory and the Node release, and it is often far below the RAM you have; when a build or server reaches it, V8 aborts with FATAL ERROR: Reached heap limit and exit code 134. Read the real limit with v8.getHeapStatistics().heap_size_limit, then raise it with --max-old-space-size (in MiB) or NODE_OPTIONS for a large workload, size it below the cgroup limit inside containers, and use --heapsnapshot-near-heap-limit to catch a leak in a long-running process. Exit 137 with no FATAL ERROR line is a container kill, not this.

Node.js
exec /docker-entrypoint.sh: exec format errorFixed

Docker: "exec format error" when the container starts — wrong-platform image, no emulator, or a script with no shebang

The container exits on its first instruction with exec format error — the kernel's ENOEXEC, meaning the file exists but cannot be executed here. In practice that is an image built on one CPU architecture (an Apple-silicon Mac produces linux/arm64) and run on another (an x86_64 server) with no QEMU handler registered in binfmt_misc, or an entrypoint script whose first line is not a shebang. uname -m, docker image inspect and ls /proc/sys/fs/binfmt_misc tell the causes apart; the fix is an explicit docker buildx build --platform (or a manifest list for both), QEMU registration or --platform when you mean to emulate, and a #!/bin/sh line for the script.

Docker