Deploy Error Decoder

ERR_REQUIRE_ESM — require() of an ES Module

Quick answer: a package you require() is now ESM-only and cannot be loaded with require(). Three fixes: pin the last CommonJS version (e.g. node-fetch@2, chalk@4), convert your project to ESM ("type": "module" + import), or load it with a dynamic import() from your CommonJS code.

Not your exact error? Paste it into the Deploy Error Decoder →

What the error looks like

Node names the ESM package and even suggests the fix:

Error [ERR_REQUIRE_ESM]: require() of ES Module
/app/node_modules/node-fetch/src/index.js from /app/server.js not supported.
Instead change the require of index.js to a dynamic import() which is
available in all CommonJS modules.

The package in the path (here node-fetch) went ESM-only, and your file used require() to load it.

Why it happens

The package went ESM-only

node-fetch 3, chalk 5, nanoid 4, got 12 and others dropped CommonJS in a major release.

Your project is CommonJS

require() is synchronous and cannot load an ES module, which is asynchronous.

A transitive dep pulled ESM

An update bumped a dependency to an ESM-only version somewhere in the tree.

Mixed module systems

CommonJS and ESM in one project without the right package.json type or file extensions.

Diagnose it in three steps

1

Identify the ESM package

# the error path names it - e.g. node-fetch. Check its package.json:
cat node_modules/node-fetch/package.json | grep -E '"type"|"exports"'
# "type": "module" (and no CommonJS export) = ESM-only.
2

Check your project's module system

grep '"type"' package.json
# missing or "commonjs" = you are CommonJS and cannot require() ESM.
3

Pick a fix: pin, convert, or dynamic import

# fastest: pin the last CommonJS major
npm install node-fetch@2 chalk@4
The real fix

Pin it, convert to ESM, or dynamic import()

Three valid paths. Pinning the CommonJS version unblocks you immediately; converting to ESM is the durable, forward path; a dynamic import() lets a CommonJS file load the ESM package without a full migration.

// A) Pin the last CommonJS version (fastest)
npm install node-fetch@2 chalk@4

// B) Convert your project to ESM - package.json:
{ "type": "module" }
// then use import, not require:
import fetch from 'node-fetch';

// C) Dynamic import() from CommonJS (no migration):
async function getData() {
  const { default: fetch } = await import('node-fetch');
  return fetch('https://example.com');
}

For a new project, start with ESM. For an established CommonJS app on a deadline, pin the version and plan the ESM migration separately so it does not block the deploy.

How Infraveil handles this

Pinned dependencies, caught before prod

An ESM-only bump that breaks require() is a dependency-drift problem - an unpinned update changed behavior under you. On your own servers, Infraveil runs deploys from a pinned, reproducible build and catches a failed start before it serves traffic, so a surprise ESM bump shows up in the pipeline, not in production - with recovery approval-gated and recorded.

Reproducible builds from a pinned lock file, on infra you control
A failed start caught and surfaced before traffic flows
Recovery approval-gated and recorded

Frequently asked questions

What does ERR_REQUIRE_ESM mean?

You used require() on a package that ships only as an ES module. require() cannot load ESM, so Node throws and suggests a dynamic import() instead.

Why did this start after a package update?

The package released a major that dropped CommonJS and went ESM-only - node-fetch 3, chalk 5, nanoid 4 and others did this. Pin the previous major or migrate to ESM.

Should I convert my project to ESM?

It is the forward path and the better default for new projects, but it is a migration for an existing CommonJS app. For a quick unblock, pin the CommonJS version of the package.

Can I require() an ESM module at all?

Not directly. From CommonJS use a dynamic import(), which returns a promise: const { default: x } = await import('pkg'). To use plain import syntax, the file itself must be ESM.

Free tools for this

Client-side, no signup — they run in your browser.