Skip to content
Latchkey

Node ERR_REQUIRE_ESM "Must use import to load ES Module" - Fix in CI

ERR_REQUIRE_ESM means CommonJS code tried to require() a package that is ESM-only. Many popular packages have gone ESM-only, breaking projects that still use require.

What this error means

A CommonJS project (or compiled-to-CJS output) crashes with ERR_REQUIRE_ESM when loading a dependency that ships only ESM. It often appears right after upgrading such a dependency to a major that dropped CommonJS.

Node output
Error [ERR_REQUIRE_ESM]: require() of ES Module
/app/node_modules/chalk/source/index.js from /app/index.js not supported.
Instead change the require of index.js to a dynamic import() which is
available in all CommonJS modules.

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

The dependency is ESM-only

A package published an ESM-only major (no CommonJS entry). CommonJS require() cannot load it synchronously.

Your project (or its build output) is CommonJS

If your package is CJS - or TS compiles to CJS - a plain require of the ESM package fails.

How to fix it

Move to ESM or use dynamic import

Convert your module to ESM, or load the ESM-only package with await import().

JS
// CJS interop without converting everything:
const chalk = (await import('chalk')).default;

// or make the package ESM:
// package.json -> { "type": "module" }

Pin a CJS-compatible version if you must

  1. If converting is not feasible now, pin the last dependency major that still shipped CommonJS.
  2. Plan the ESM migration (set "type": "module", fix imports) rather than pinning forever.
  3. For TS, target ESM output (NodeNext) to consume ESM-only deps cleanly.

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

  • Migrate projects to ESM as the ecosystem moves.
  • Use dynamic import() for ESM-only deps in CJS code.
  • Read release notes before bumping a dep to an ESM-only major.

Frequently asked questions

What causes Node ERR_REQUIRE_ESM "Must use import to load ES Module"?
There are 2 common causes: the dependency is esm-only and your project (or its build output) is commonjs. A package published an ESM-only major (no CommonJS entry).
How do I fix Node ERR_REQUIRE_ESM "Must use import to load ES Module"?
There are 2 fixes depending on which cause you have: move to esm or use dynamic import and pin a cjs-compatible version if you must. Work through them in order, since the first is the most common.
What does Node ERR_REQUIRE_ESM "Must use import to load ES Module" actually mean?
A CommonJS project (or compiled-to-CJS output) crashes with ERR_REQUIRE_ESM when loading a dependency that ships only ESM.
How do I stop Node ERR_REQUIRE_ESM "Must use import to load ES Module" happening again?
Migrate projects to ESM as the ecosystem moves. 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