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.
# The fastest one-liner to unblock a stuck install
npm install --legacy-peer-depsIf 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.
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 resolveThe 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.
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 occurredFound: react@18.2.0→ the version already locked in the current treeCould 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.
| Approach | Command / config | Behavior | Side effects | When to use |
|---|---|---|---|---|
--legacy-peer-deps | npm install --legacy-peer-deps | Skip peer checks the npm v6 way | Potential runtime compatibility risk | Urgent build recovery; fine in most cases |
--force | npm install --force | Ignore conflicts and force the install | Leaves a broken tree as-is | Last resort |
overrides | JSON config in package.json | Pin a specific nested dependency version | Runtime errors if you pin the wrong version | Surgical fix; recommended for teams |
| Version alignment | Match versions yourself | Root-cause fix that satisfies peer requirements | Takes time; may require code changes | Permanent fix |
1) --legacy-peer-deps (the most reasonable temporary escape hatch)
npm install --legacy-peer-depsIf you don’t want to type it every time, put it in .npmrc at the project root.
# .npmrc
legacy-peer-deps=true2) --force (only when you are truly desperate)
npm install --forceIt 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.
{
"overrides": {
"some-legacy-lib": {
"react": "$react"
},
"another-lib": {
"react-dom": "18.2.0"
}
}
}"$react" means “follow whatever react version the root project uses.” 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.
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.
# 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 ciCommitting .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.
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.
{ "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.
rm -rf node_modules package-lock.json
npm cache verify
npm installResetting 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).
Conclusion: a decision checklist by situation
Apply these in urgency order, but do not stay on the workaround—follow the roadmap.
- You only need the build unblocked right now →
npm install --legacy-peer-deps - You have identified the offending package → pin it with
package.jsonoverrides - It only breaks in CI → unify the npm version (
engines/.nvmrc) and commit.npmrc - The lock is completely tangled → clean reset (PR required if the team shares it)
- 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. Almost always 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.
Nodelog는 모든 콘텐츠의 내용과 출처를 공개 전에 검토합니다. 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서와 함께 확인하며, 검토 기준과 정정 원칙은 편집 정책에서 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
Comments
Be the first to comment.