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.
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.
- run: |
node --version && npm --version
echo "NODE_ENV=$NODE_ENV CI=$CI"
nproc && free -h && df -h /
ls -la node_modules/.bin | headCommon 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().
// 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
- If converting is not feasible now, pin the last dependency major that still shipped CommonJS.
- Plan the ESM migration (set
"type": "module", fix imports) rather than pinning forever. - 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-sizeor use a larger runner. - devDependencies pruned.
NODE_ENV=productionmakesnpm ciskip 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.