Skip to content
Latchkey

npm EBADENGINE "Unsupported engine" in CI - Fix Node Version Mismatch

EBADENGINE means a package declares an engines range that the current Node or npm version does not satisfy. It warns by default but can hard-fail when engine-strict is on.

What this error means

During install npm prints EBADENGINE listing the package, the wanted Node range, and the current version. With engine-strict enabled the install aborts instead of warning.

npm
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'vite@5.0.0',
npm WARN EBADENGINE   required: { node: '^18.0.0 || >=20.0.0' },
npm WARN EBADENGINE   current: { node: 'v16.20.2', npm: '8.19.4' }
npm WARN EBADENGINE }

Diagnose it: reproduce the CI install locally

Install failures are usually environment drift rather than a broken lockfile: a different package-manager major, a different Node version, or a cache that is being restored from a run with different inputs. Reproduce the CI conditions before changing the lockfile, because regenerating it hides the real cause.

Terminal
# match the runner exactly, then install from a clean slate
node --version && npm --version
rm -rf node_modules
npm ci --foreground-scripts

# if that succeeds locally but fails in CI, the difference is the cache
# or the package-manager version, not your lockfile

Common causes

The CI Node version is older than dependencies require

The runner uses a Node major below the engines range a dependency declares, so npm flags the mismatch.

engine-strict turns the warning into a failure

With engine-strict=true in .npmrc, an unsupported engine aborts the install rather than warning.

How to fix it

Pin a supported Node version in CI

  1. Set the Node version in the setup step to a value inside the required range.
  2. Re-run the install.
Workflow
- uses: actions/setup-node@v4
  with:
    node-version: 20

Verify the fix survives a cold cache

A green run immediately after a fix often proves nothing, because it restored a cache written before the change. Force a cold install once to confirm the fix is real.

.github/workflows/ci.yml
# temporarily bust the cache key to prove the fix on a cold runner
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: npm
    cache-dependency-path: package-lock.json
# then bump this suffix once, run, and remove it
#   key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}-v2

How to prevent it

  • Declare your own engines field, pin the Node version in setup-node, and keep it inside the intersection of every dependency engines range so installs stay clean.

Frequently asked questions

What causes npm EBADENGINE "Unsupported engine" in CI?
There are 2 common causes: the ci node version is older than dependencies require and engine-strict turns the warning into a failure. The runner uses a Node major below the engines range a dependency declares, so npm flags the mismatch.
How do I fix npm EBADENGINE "Unsupported engine" in CI?
Pin a supported Node version in CI. Set the Node version in the setup step to a value inside the required range.
What does npm EBADENGINE "Unsupported engine" in CI actually mean?
During install npm prints EBADENGINE listing the package, the wanted Node range, and the current version.
How do I stop npm EBADENGINE "Unsupported engine" in CI happening again?
Declare your own engines field, pin the Node version in setup-node, and keep it inside the intersection of every dependency engines range so installs stay clean.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card