require is not defined in ES module scope (tested)
ReferenceError: require is not defined in ES module scope, you can use import instead means a file is running as an ES module (ESM) but calls the CommonJS-only function require(). We reproduced it on Node.js 24.21.0, along with 3 related errors people search for in the same confusion — and found that Node's own rules changed recently enough that a lot of existing advice about them is now out of date.
If you want the short version: a file is ESM if its nearest package.json has "type": "module", or the file ends in .mjs. Otherwise it is CommonJS, unless it ends in .cjs, which forces CommonJS no matter what.
1. The error, reproduced
With package.json containing {"type": "module"}, this file:
const fs = require('fs');
console.log(fs);
fails immediately on Node 24.21.0:
file:///tmp/esmtest/a/bad.js:1
const fs = require('fs');
^
ReferenceError: require is not defined in ES module scope, you can use import instead
The fix is exactly what the message says: use import.
import fs from 'fs';
console.log(fs);
2. The fix depends on why the file is ESM
| Why the file is ESM | Fix |
|---|---|
Nearest package.json has "type": "module" and you only meant this one file to be CommonJS | Rename the file to .cjs — that extension forces CommonJS regardless of type. |
File extension is .mjs | Either rename to .cjs/plain .js without type:module, or convert the require() calls to import. |
| You actually want ESM everywhere (modern code, tree-shaking, top-level await) | Convert to import/export throughout. There is no partial mode: a file is fully ESM or fully CommonJS. |
3. A related error that no longer reproduces the way you'd expect: "Cannot use import statement outside a module"
This is the mirror-image mistake: import syntax in a file Node thinks is CommonJS. On older Node versions this reliably threw SyntaxError: Cannot use import statement outside a module. On Node 24.21.0 with a plain .js file and no "type": "module", it does not throw that error any more:
$ node bad.js
(node:2117527) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///tmp/esmtest/b/bad.js is not specified and it doesn't parse as CommonJS.
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
To eliminate this warning, add "type": "module" to /tmp/esmtest/b/package.json.
{ appendFile: [Function: appendFile], ... }
Node now auto-detects ESM syntax in typeless .js files and silently reparses the file as a module, only warning about the performance cost. It works, but it is slower to start and the warning will show up in your logs. The classic hard error still happens if you force CommonJS parsing by using a .cjs extension:
$ node importer.cjs
/tmp/esmtest/c/importer.cjs:1
import fs from 'fs';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at wrapSafe (node:internal/modules/cjs/loader:1861:18)
Takeaway: if you're on Node 20.19+/22.12+/24 and don't see this error where you expected it, that's correct — add "type": "module" to silence the warning rather than chasing an error that won't appear. If you need the strict error (e.g. in a test asserting file type), use .cjs.
4. ERR_REQUIRE_ESM: also mostly gone on current Node
The third error in this family is requiring an ESM package from CommonJS. Historically this always threw Error [ERR_REQUIRE_ESM]. We built a real local ESM package ("type": "module", exporting a named function) and required it from a .cjs file on Node 24.21.0:
const { hello } = require('./esm_pkg/index.js');
console.log(hello());
// -> prints "hi" -- no error at all
This works because Node 22.12+ (unflagged; Node 24 included) added synchronous require() support for ES modules. ERR_REQUIRE_ESM still exists, but on current Node it only fires in two cases we could reproduce:
- You explicitly disable the feature:
node --no-experimental-require-module entry.cjsbrings the classic error back verbatim:Error [ERR_REQUIRE_ESM]: require() of ES Module ... not supported. Instead change the require of index.js in entry.cjs to a dynamic import() ... - You're on an older Node release (before 20.19/22.12) where the feature doesn't exist yet.
There is a fourth, genuinely current failure mode this uncovers: if the required ESM module has a top-level await, synchronous require() cannot wait for it, so Node throws a different, newer error:
Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph with top-level await. Use import() instead. To see where the top-level await comes from, use --experimental-print-required-tla.
That one you do still need to fix with a dynamic import() — there's no way to require() your way out of real top-level await.
5. "SyntaxError: Unexpected token 'export'": usually a tooling/transform problem, not a Node core one
This string shows up most in test runners (Jest with the default CommonJS transform is the single biggest source, per the autocomplete data behind this guide) and older bundler setups, when a require()-based tool loads a file that uses export syntax and the tool's own parser (not plain Node) chokes on it. We reproduced the core mechanism without needing Jest installed: requiring any file containing export syntax through a code path that forces CommonJS parsing hits this before Node even gets to module-type detection, since export is as much a syntax error in CommonJS as import is.
Fixes, in order of how often they apply: (1) for Jest, set "type": "module" in the package under test and use Jest's ESM mode, or add the offending package to transformIgnorePatterns exceptions so Babel/ts-jest transpiles it; (2) for a plain script, make sure the file importing it is itself ESM (type:module or .mjs) so export is valid syntax in context; (3) check you don't have a stray .cjs extension forcing CommonJS on a file that's meant to be ESM.
6. Quick reference
| Error | Means | Still happens on Node 24 by default? |
|---|---|---|
| require is not defined in ES module scope | require() called inside a real ES module | Yes, always |
| Cannot use import statement outside a module | import/export syntax where Node expects CommonJS | Only with a .cjs file; plain typeless .js now auto-detects and warns instead |
| ERR_REQUIRE_ESM | require() of an ESM package | Only with --no-experimental-require-module, older Node, or packages Node can't load synchronously |
| ERR_REQUIRE_ASYNC_MODULE | require() of an ESM module with top-level await | Yes, always — use import() |
See also TypeScript: Cannot find module and Vite: Failed to resolve import for the two most common neighbouring build-tool errors.
Frequently asked questions
Is "require is not defined in ES module scope" a bug in my code or in Node?
Neither — it's Node correctly telling you the file is running as an ES module (because of "type": "module" or a .mjs extension), so the CommonJS-only require() function doesn't exist in that scope. Switch to import, or force the file back to CommonJS with a .cjs extension.
Why didn't I get "Cannot use import statement outside a module" like the error message online says?
On Node 20.19+/22.12+/24, a plain typeless .js file with import/export syntax is now auto-detected as an ES module and silently reparsed, with only a performance warning — not a thrown error. We confirmed this on Node 24.21.0. The hard SyntaxError still fires if the file has a .cjs extension, which forces CommonJS parsing and skips detection.
Does require() of an ES module still throw ERR_REQUIRE_ESM?
Not by default on current Node (22.12+/24): we required a real ESM package from a .cjs file on Node 24.21.0 and it worked with no error. ERR_REQUIRE_ESM still exists for the --no-experimental-require-module flag, older Node versions, and ESM modules with top-level await (which throw the newer ERR_REQUIRE_ASYNC_MODULE instead).
What's the one case where require() of an ESM module still fails on current Node?
Top-level await. We measured it: requiring an ESM file containing a top-level await throws Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph with top-level await. Use import() instead, and that advice is correct — switch that one call site to a dynamic import().
How do I tell Node a file must always be CommonJS or always be ESM, regardless of package.json?
Use the file extension: .cjs always forces CommonJS parsing (require/module.exports, no top-level import/export), and .mjs always forces ES module parsing (import/export, no require), no matter what "type" says in the nearest package.json.
Written with AI assistance. Measured on 2026-10-02 with Node.js 24.21.0 on Linux. Every example below is a real run in /tmp, not a quote from docs. Node.js is free and open source.