Skip to content
Latchkey

Node.js "Cannot find module 'node-fetch'" (ESM-only v3) in CI

node-fetch v3+ is published as ESM only. CommonJS code that require("node-fetch") either cannot resolve it or throws ERR_REQUIRE_ESM, breaking the build.

What this error means

After upgrading node-fetch, a CommonJS project fails on require("node-fetch") with a module-not-found or ERR_REQUIRE_ESM error in CI.

node
Error [ERR_REQUIRE_ESM]: require() of ES Module
/work/repo/node_modules/node-fetch/src/index.js from
/work/repo/src/http.js not supported.
  code: 'ERR_REQUIRE_ESM'

Diagnose it: what is different about the runner?

A build that passes locally and fails on a runner differs in a small number of predictable ways. Check those before changing build configuration, because the build config is usually not the thing that changed.

.github/workflows/ci.yml
- run: |
    node --version && npm --version
    echo "NODE_ENV=$NODE_ENV  CI=$CI"
    nproc && free -h && df -h /
    ls -la node_modules/.bin | head

Common causes

node-fetch v3 dropped CommonJS

Version 3 ships ESM only. A CommonJS project that upgrades can no longer require it synchronously.

Native fetch makes node-fetch unnecessary

Modern Node has a global fetch. Keeping node-fetch at all is often avoidable and the source of the ESM mismatch.

How to fix it

Use the built-in global fetch

Drop the dependency entirely on Node 18+, which provides fetch globally.

src/http.js
// remove node-fetch import
const res = await fetch('https://example.com');
const data = await res.json();

Or pin node-fetch v2 for CommonJS

If you must keep node-fetch in a CommonJS project, pin v2, which still ships CommonJS.

Terminal
npm install node-fetch@2

The three that account for most of them

  • Case sensitivity. Linux runners are case sensitive, macOS is not. An import with the wrong case resolves locally and fails in CI.
  • Out of memory. Exit code 137 is a SIGKILL from the kernel, not a build error. Raise --max-old-space-size or use a larger runner.
  • devDependencies pruned. NODE_ENV=production makes npm ci skip devDependencies, so the build tool itself goes missing. Set it after install, not before.

How to prevent it

  • Prefer the global fetch on Node 18+ and remove node-fetch.
  • Read changelogs for ESM-only majors before upgrading.
  • Keep one module system across the project.

Frequently asked questions

What causes Node.js "Cannot find module 'node-fetch'" (ESM-only v3) in CI?
There are 2 common causes: node-fetch v3 dropped commonjs and native fetch makes node-fetch unnecessary. Version 3 ships ESM only.
How do I fix Node.js "Cannot find module 'node-fetch'" (ESM-only v3) in CI?
There are 2 fixes depending on which cause you have: use the built-in global fetch and or pin node-fetch v2 for commonjs. Work through them in order, since the first is the most common.
What does Node.js "Cannot find module 'node-fetch'" (ESM-only v3) in CI actually mean?
After upgrading node-fetch, a CommonJS project fails on require("node-fetch") with a module-not-found or ERR_REQUIRE_ESM error in CI.
How do I stop Node.js "Cannot find module 'node-fetch'" (ESM-only v3) in CI happening again?
Prefer the global fetch on Node 18+ and remove node-fetch. The prevention section lists 3 changes that keep it from recurring.

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