An ERESOLVE error means npm could not find a dependency tree that satisfies the package requirements it is checking. The message can look intimidating, but it usually names the package that has a requirement and the package version that fails to meet it. This guide shows how to trace that mismatch, make a targeted change, and verify the result without blindly bypassing npm’s checks.
Key Takeaways
- Read the
ERESOLVEoutput for the package with the peer requirement and the version npm found instead. - Use
npm explain <package>to see why a package is in your dependency tree; usenpm lsto spot invalid or conflicting entries. - Prefer a compatible package version or a deliberate dependency upgrade over
--forceor--legacy-peer-deps. - Keep the lockfile and use
npm cito check that the committed dependency set installs reproducibly.
What an npm peer dependency conflict means
A package can declare dependencies it needs to run and peer dependencies it expects the consuming project to provide. Peer dependencies commonly express compatibility with a host package or plugin system. For example, a UI component library might declare that it supports a particular range of React versions. The application supplies React; the library declares which versions it is compatible with.
npm uses semantic version ranges to describe acceptable versions. A range such as ^18.2.0 generally allows versions from 18.2.0 up to, but not including, 19.0.0. A peer requirement of ^18 therefore does not accept React 19. When the project and a package’s peer range cannot be reconciled, npm may stop with ERESOLVE rather than silently install an incompatible combination.
The exact rules depend on the package metadata and npm version. Check the project’s npm version with npm --version before comparing behavior or reproducing an install on another machine.
Read the error before changing dependencies
An error may include lines resembling these:
Found: react@19.0.0
Could not resolve dependency:
peer react@"^18.0.0" from example-widget@2.4.0
This example identifies the mismatch: the project has React 19, while example-widget@2.4.0 declares a peer range that accepts React 18, not React 19. The package names and versions in a real error will differ. Look for:
Found:— the version npm found in the project’s dependency tree.Could not resolve dependency:— the package and peer range that could not be satisfied.- The dependency path — when present, it can show which package introduced the requirement.
Do not assume that the first package named in the output is the one to downgrade. A shared dependency can be used by several packages, and changing it may create another conflict.
Trace why a package is installed
Run these commands from the project directory:
npm explain react
npm ls react
Replace react with the package named in the error. npm explain describes why a package is present in the dependency tree. npm ls shows installed versions and can mark entries as invalid when they do not satisfy a declared requirement. These commands help identify whether the package is a direct dependency or was brought in by another package.
If the conflict concerns a plugin’s peer range, inspect the plugin’s published metadata too:
npm view example-widget@2.4.0 peerDependencies
Use the actual package name and version from the error. This command queries registry metadata; it does not prove that a package works with a version it does not declare. For a private package or a project using a non-default registry, make sure the command is querying the registry that supplies that package.
Choose a targeted fix
Once you know which requirement conflicts, select a fix that matches the project’s compatibility needs.
Update the package that has the restrictive peer range
Check whether a newer release of the plugin or integration declares support for the host version your project uses. Review that release’s documentation and changelog, then update it deliberately. Avoid assuming that the newest version is automatically compatible with every other dependency.
For example, if a newer widget release supports React 19, update the widget to that compatible release and review the resulting dependency changes:
npm install example-widget@<compatible-version>
Replace the placeholder with a real version you have verified. Review the changes to package.json and package-lock.json before committing them.
Use a compatible host version when the project requires it
If the plugin has no release compatible with your current host version and the plugin is essential, you may need to use a host version within its declared peer range. Check the impact on the rest of the application before changing a core dependency; other packages may require the newer version.
Replace or remove an incompatible plugin
If a package is unmaintained, unnecessary, or has no compatible release, consider a maintained alternative or remove it. This is often safer than forcing an install and discovering incompatibility at runtime.
Check your own package’s peer range
If you maintain a library, make sure its peerDependencies accurately describe the versions it supports. Do not widen a range just to make installation succeed unless the package has actually been checked against those versions. A permissive but incorrect range shifts the compatibility problem to library users.
Keep lockfiles and installs reproducible
The lockfile records the resolved dependency tree so installs can reproduce the project’s selected versions. Do not delete package-lock.json as a first response to ERESOLVE: removing it discards useful resolution information and can produce a different dependency tree without fixing the underlying incompatible peer requirement.
For a project with a committed npm lockfile, use npm ci to test the locked install in a clean dependency directory. It removes the existing node_modules directory and fails if the lockfile and package.json are out of sync; it does not rewrite the lockfile to resolve that mismatch.
A practical workflow is:
# Start from a clean working tree, or save your current changes first.
git status
# Reproduce the install represented by the committed lockfile.
npm ci
# After making a targeted dependency change, update the lockfile.
npm install
# Review the dependency changes, then verify the lockfile install.
git diff -- package.json package-lock.json
npm ci
If npm ci fails because the manifest and lockfile disagree, use npm install after the intended manifest change to update the lockfile, inspect the diff, and then run npm ci again. Commit the manifest and lockfile together. For teams, use a consistent Node.js and npm setup so differences in tooling do not complicate installation diagnosis.
When legacy-peer-deps or force might be considered
These flags change npm’s safeguards; neither repairs an incompatible package.
--legacy-peer-depstells npm to ignore peer dependencies when building the dependency tree, using behavior associated with older npm versions. It may get an install past a peer conflict, but the resulting combination may not be supported by the packages involved. If used temporarily to investigate an existing project, record why, test the application, and avoid making it an undocumented team-wide default.--forcedisables several npm protections and can allow conflicting peer dependencies at the project root. It is broader than a targeted version correction and can leave you with a tree that installs but fails during build or at runtime.
If a project’s existing lockfile was created with a specific resolver setting, a clean install may require the same setting. Document the reason and configure it consistently for the team and CI rather than having each developer choose flags at random. Treat that as a compatibility workaround to review, not evidence that the dependency combination is valid.
Common Mistakes
- Deleting the lockfile immediately: this changes the resolution inputs and can obscure the original conflict.
- Upgrading or downgrading the package named first: first identify which package declares the unsatisfied range and how it entered the tree.
- Treating
npm explainas a compatibility check: it explains dependency paths; it does not certify that two packages work together. - Assuming installation success proves runtime compatibility: a forced or legacy install can still produce build errors, missing APIs, or incorrect behavior.
- Using a flag locally but not in CI: differences in install settings can make the project appear reproducible when it is not.
Best Practices
- Capture the full error and note the npm version.
- Trace the relevant packages with
npm explainandnpm ls. - Confirm the peer range from package metadata and relevant package documentation.
- Prefer a compatible release, a planned host-version change, or a replacement package.
- Review lockfile changes and verify the final state with
npm ci. - If a bypass is unavoidable, document its scope, test the application, and plan to remove the workaround.
Conclusion
An ERESOLVE message is a dependency compatibility clue, not a reason to reach for --force automatically. Identify the peer requirement, trace the packages involved, make the smallest justified change, and confirm that the lockfile reproduces the install.
References
- npm install — install behavior and dependency resolution.
- npm explain — why a package is present in the dependency tree.
- npm package.json documentation — package dependency fields, including peer dependencies.
- npm configuration:
legacy-peer-depsandforce— npm configuration options and their effects.
