Web Architecture

npm ERESOLVE: Works Locally, Fails on Vercel

A security upgrade installed fine locally, then failed on Vercel in seconds. Why npm only warns on your machine, and the fix that isn't --legacy-peer-deps.

October 24, 20267 min read
npmVercelpeer dependenciesReactNext.js

The short answer: when you install a named version, such as npm install react@^19.3.1, npm resolves a peer-dependency conflict with a warning ("ERESOLVE overriding peer dependency") and writes the result to package.json and the lockfile. A fresh install of those same files has no override to fall back on, so npm install and npm ci fail with ERESOLVE. That's why it works on your machine and fails on Vercel or CI. The fix is to make the versions agree: upgrade the package whose peer range is too narrow, or pin the one you upgraded. --legacy-peer-deps and --force only hide the conflict.

This happened on this site on 24 October 2026, during a routine Next.js security upgrade. The steps below come from that incident, reproduced afterwards from a clean copy of the repository.

A peer dependency is a package that a library expects you to install yourself, at a version inside a range the library declares. A 3D library built for React 19.2 declares React as a peer, with a range that says which React versions it has been tested with.

What happened

Next.js published security fixes on 14 October 2026, and React patched its own releases the same day. The upgrade looked like a one-liner:

npm install next@^16.4.3 react@^19.2.9 react-dom@^19.2.9

On a machine with an existing node_modules, it succeeded. It printed four warnings, which were easy to miss in a long install log:

npm warn ERESOLVE overriding peer dependency
npm warn   peer react@"^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" from @ai-sdk/react@4.0.12
npm warn peer react@">=19 <19.3" from @react-three/fiber@9.6.1

Because ^19.2.9 also matches 19.3.1, npm installed React 19.3.1 and saved "react": "^19.3.1". The build passed and every page generated. It was pushed.

Vercel's preview deployment failed about seven seconds after the build started, before compiling a line:

npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error peer react@">=19 <19.3" from @react-three/fiber@9.6.1
Error: Command "npm install" exited with 1

Why did it pass locally?

npm's documentation for the strict-peer-deps setting describes the default behaviour. Conflicting peer dependencies "will be resolved using the nearest non-peer dependency specification, even if doing so will result in some packages receiving a peer dependency outside the range". It adds: "When such an override is performed, a warning is printed."

So the local install did exactly what npm promises. It honoured the version that was asked for, overrode the 3D library's peer range, and warned. A fresh install from the committed files behaved differently:

ScenarioCommandResult
Upgrade on a working machinenpm install react@^19.2.9 …Passed with ERESOLVE overriding warnings
Vercel buildnpm install (fresh)Failed: ERESOLVE
Clean copy, npm 10.9.4npm installFailed: ERESOLVE
Clean copy, npm 10.9.4npm ciFailed: ERESOLVE
Clean copy, npm 12.2.1npm ciFailed: ERESOLVE

The lesson isn't that npm is inconsistent. The warning was the failure, delivered early and quietly.

Why didn't npm just upgrade the 3D library?

It could have. package.json asked for @react-three/fiber ^9.6.1, and version 9.8.0, published on 22 September 2026, widened its React peer range to >=19 <19.4. That covers React 19.3, which had been out since 9 September.

But the lockfile pinned fiber at 9.6.1, and npm doesn't move a locked package to a newer version to settle a peer conflict. The fix was available inside the allowed range. It just had to be asked for.

How to fix ERESOLVE properly

  1. Read the two ranges in the error. It names the package whose peer range you broke (@react-three/fiber@9.6.1 wants React <19.3) and the version you have (react@19.3.1).
  2. Check for a newer version of the narrow package. Run npm view @react-three/fiber@latest peerDependencies. If a newer release accepts your React, upgrade it in the same commit: npm install @react-three/fiber@^9.8.0.
  3. Otherwise, pin the package you upgraded. This site pinned react and react-dom to ~19.2.9, which carries the 14 October React security patch and stays inside fiber's range. It's the smaller change for a security-only pull request. Bump fiber and React together later, deliberately.
  4. Reproduce CI before you push. Copy package.json and package-lock.json into an empty directory and run npm ci --ignore-scripts. If that passes, Vercel's install will too.
  5. Read the warnings. Treat ERESOLVE overriding peer dependency as a failure. Setting strict-peer-deps=true in a project .npmrc makes npm do that for you.

Why not --legacy-peer-deps?

Because it switches the check off rather than resolving it. npm's documentation says legacy-peer-deps causes npm "to completely ignore peerDependencies when building a package tree, as in npm versions 3 through 6". It also says use of the flag "is not recommended, as it will not enforce the peerDependencies contract that meta-dependencies may rely on."

Setting it on Vercel would have deployed a React version the 3D library had never declared support for. The build might have worked. The 3D floor-plan page might have broken in the browser, where no build log would show it. --force has the same problem with an even wider blast radius.

Common questions

Why does npm install work locally but fail on Vercel?

Because npm resolves peer-dependency conflicts with a warning when you install a named version, but a fresh install of the resulting package.json and lockfile has no such override and fails with ERESOLVE. Vercel installs fresh. Reproduce it locally with npm ci in an empty directory.

What does "npm warn ERESOLVE overriding peer dependency" mean?

npm installed a version outside a package's declared peer range because you asked for it explicitly, and printed a warning instead of failing. npm's documentation describes this as the default when strict-peer-deps is off. Treat it as a build that will fail somewhere else.

Should I use --legacy-peer-deps on Vercel?

Not as a fix. npm's documentation says the flag ignores peer dependencies entirely and is not recommended, because it doesn't enforce the peer contract packages rely on. It can deploy incompatible versions that fail at runtime instead of at install. Align the versions instead.

How do I test a dependency change before pushing?

Copy package.json and package-lock.json into an empty directory and run npm ci --ignore-scripts. That matches a clean CI install without running install scripts. If it fails with ERESOLVE, fix the versions locally before pushing.

The honest summary

Most "works on my machine" install failures are a warning that was printed and ignored. npm is consistent: it overrides with a warning when you ask for a version, and refuses when it has to work it out from your files. Read the ranges in the error, upgrade the narrow package if a newer one fits, and run a clean npm ci before pushing any dependency change.

If upgrades like this keep turning into incidents, that usually means the stack needs a maintenance routine rather than more heroics. That's part of what a web architecture consultant sets up. The wider upgrade picture is in upgrading from Next.js 15 to 16 and what changed in npm in 2026.

Sources: npm CLI config definitions: strict-peer-deps, legacy-peer-deps · @react-three/fiber on npm · Next.js security update, 14 October 2026


Written by Jagatjeet, a web architect and digital marketer based in Kamloops, BC. Jagatjeet (jagatjeet.com) builds and maintains Next.js sites for businesses and founders. Published 24 October 2026.

New posts by email

Local SEO, web design, and digital marketing for BC Interior businesses. When a new post publishes — not on a schedule.

Apply

If this maps to a problem you're working on.

I work with $1M–$20M ARR founders whose digital investment isn't producing the return it should. Applications reviewed personally within 48 hours.

2 Diagnostic slots / month · 2–3 full engagements / quarter · 48h review