This error occurs when attempting to use require() to import an ES module in a CommonJS context. Node.js enforces strict module system boundaries, and ES modules must be imported using import statements or dynamic import().
The ERR_REQUIRE_ESM error is Node.js's way of enforcing the fundamental incompatibility between CommonJS's synchronous require() function and ES modules. When a package is published as an ES module (either through "type": "module" in package.json or using .mjs file extensions), it cannot be loaded using the traditional require() syntax. This error became increasingly common as the Node.js ecosystem migrated toward ES modules. Many popular packages, such as node-fetch version 3+, chalk version 5+, and got version 12+, have adopted ESM-only distributions, meaning older projects using CommonJS suddenly encounter this error when upgrading dependencies. The core issue stems from the different loading mechanisms: require() is synchronous and expects CommonJS modules, while ES modules support asynchronous operations like top-level await and have different scoping rules. Node.js cannot simply convert between these formats at runtime without breaking fundamental guarantees of each system.
Examine the error stack trace to identify the exact module that cannot be loaded:
Error [ERR_REQUIRE_ESM]: require() of ES Module /node_modules/package-name/index.js from ./your-file.js not supported.
Instead change the require of /node_modules/package-name/index.js to a dynamic import() which is available in all CommonJS modules.The error message explicitly tells you which package is ESM-only. Common culprits include:
- node-fetch (v3+)
- chalk (v5+)
- got (v12+)
- nanoid (v4+)
- open (v9+)
The most modern approach is to adopt ES modules throughout your project.
Add to your package.json:
{
"type": "module"
}Then convert all require() statements to import syntax:
// Before (CommonJS)
const fetch = require('node-fetch');
const chalk = require('chalk');
// After (ESM)
import fetch from 'node-fetch';
import chalk from 'chalk';Update your own exports:
// Before
module.exports = { myFunction };
// After
export { myFunction };Note: If you have .js files that must remain CommonJS, rename them to .cjs extensions.
If converting your entire project isn't feasible, use dynamic imports for ESM-only packages. Dynamic import() returns a promise and works in CommonJS:
// Top-level async function approach
async function loadModules() {
const fetch = await import('node-fetch');
const chalk = await import('chalk');
// Use .default for default exports
const response = await fetch.default('https://api.example.com');
console.log(chalk.default.green('Success!'));
}
loadModules();For synchronous code that needs async imports:
// Lazy loading approach
let chalk;
async function getChalk() {
if (!chalk) {
chalk = await import('chalk');
}
return chalk.default;
}
async function logMessage(message) {
const chalkModule = await getChalk();
console.log(chalkModule.green(message));
}If you cannot modify your module system, downgrade the problematic package to its last CommonJS version:
# node-fetch: v2.6.7 is last CommonJS version
npm install [email protected]
# chalk: v4.1.2 is last CommonJS version
npm install [email protected]
# got: v11.8.5 is last CommonJS version
npm install [email protected]
# nanoid: v3.3.4 is last CommonJS version
npm install [email protected]Pin the version in package.json to prevent automatic upgrades:
{
"dependencies": {
"node-fetch": "2.6.7",
"chalk": "4.1.2"
}
}Important: Older versions may lack security fixes and new features. This is a temporary solution.
If using TypeScript, update tsconfig.json to properly handle ES modules:
{
"compilerOptions": {
"module": "ES2020", // or "ES2022", "ESNext"
"moduleResolution": "node", // or "bundler" for modern projects
"esModuleInterop": true,
"target": "ES2020"
}
}Ensure your package.json includes:
{
"type": "module"
}Then use standard import syntax in your .ts files:
import fetch from 'node-fetch';
import chalk from 'chalk';TypeScript will compile these to ESM import statements rather than require() calls.
If you need both CommonJS and ESM files in the same project, use explicit file extensions:
In a "type": "module" project:
- .js and .mjs files are ES modules
- .cjs files are CommonJS
// utils.cjs (CommonJS)
const fs = require('fs');
module.exports = { readConfig };
// app.js (ESM)
import { readConfig } from './utils.cjs';
import fetch from 'node-fetch';In a "type": "commonjs" project (or no type specified):
- .js and .cjs files are CommonJS
- .mjs files are ES modules
// fetcher.mjs (ESM)
import fetch from 'node-fetch';
export { fetchData };
// app.js (CommonJS)
async function main() {
const { fetchData } = await import('./fetcher.mjs');
await fetchData();
}### Node.js 22+ Experimental require() Support
Node.js v22 introduced the experimental --experimental-require-module flag that allows require() to load synchronous ES modules:
node --experimental-require-module app.jsThis works only for ES modules without top-level await. Modules using top-level await will still throw ERR_REQUIRE_ASYNC_MODULE and must use dynamic import().
### Dual Package Exports
Library authors can support both CommonJS and ESM consumers using conditional exports in package.json:
{
"type": "module",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}This requires maintaining two build outputs. Tools like tsup, unbuild, or rollup can automate dual-format builds.
### CommonJS Cannot Import ESM Synchronously
This is a fundamental limitation: CommonJS's require() is synchronous, but ESM may contain asynchronous initialization (top-level await). The only way CommonJS can load ESM is through async dynamic import(). However, ESM can import CommonJS synchronously without issues.
### Bundlers and Build Tools
Modern bundlers (webpack 5+, Vite, esbuild, Rollup) handle ESM/CommonJS interoperability automatically in many cases. However, runtime errors can still occur if the bundler configuration doesn't match your deployment environment.
### Migration Strategy for Large Projects
For large codebases, consider:
1. Start with new files as ESM (use .mjs extension initially)
2. Create ESM entry points that dynamically import CommonJS legacy code
3. Gradually convert modules from the leaf nodes up
4. Use "type": "module" only when >80% of code is converted
5. Leverage automated tools like cjstoesm or lebab for mechanical conversions
### Package Version Compatibility Matrix
Common packages and their ESM transition versions:
| Package | Last CJS Version | First ESM-only |
|---------|------------------|----------------|
| node-fetch | 2.6.7 | 3.0.0 |
| chalk | 4.1.2 | 5.0.0 |
| got | 11.8.5 | 12.0.0 |
| nanoid | 3.3.4 | 4.0.0 |
| open | 8.4.2 | 9.0.0 |
| ora | 5.4.1 | 6.0.0 |
| execa | 5.1.1 | 6.0.0 |
Error: EMFILE: too many open files, watch
EMFILE: fs.watch() limit exceeded
Error: Middleware next() called multiple times (next() invoked twice)
Express middleware next() called multiple times
Error: Worker failed to initialize (worker startup error)
Worker failed to initialize in Node.js
Error: EMFILE: too many open files, open 'file.txt'
EMFILE: too many open files
Error: cluster.fork() failed (cannot create child process)
cluster.fork() failed - Cannot create child process