The Two ESM/CommonJS Errors Everyone Quotes Are Gone. One Nobody Mentions Is Not.
require() of an ESM module works now, and TypeScript no longer complains either. What still breaks is the dual package hazard — and it fails as instanceof returning false, not as an error.

Search for an ESM/CommonJS error and you get thousands of confident answers. Most of them are correct — about a version of Node from three years ago. The specific advice they give you now fixes a problem you do not have.
The famous error does not happen any more
Measured on Node v25.9.0, with real packages and real package.json files:
require() of an ESM module succeeds. No ERR_REQUIRE_ESM.
What you get back is the module namespace, not the default export:
keys: Token, __esModule, default, name
m[Symbol.toStringTag] === 'Module'
That distinction matters more than the fix everyone remembers. Code carried over from
the old world reaches for .default, and .default is now the default export sitting
as one property among the named ones — usually not the thing it wanted.
Neither does the TypeScript one
TS1479 — the current file is a CommonJS module whose imports will produce ‘require’
calls; however, the referenced file is an ECMAScript module — is the compile-time half
of the same folklore.
On TypeScript 6.0.3 with module: nodenext, a .cts file importing a .mjs file
produces no diagnostic at all. I checked that the file was actually being
type-checked by putting a deliberate type error in it first:
main.cts(2,14): error TS2322: Type 'number' is not assignable to type 'string'.
The control fires, the interop error does not.
What actually breaks now
What came back
keys: Token, default, name · m.default === 'i-am-a-property-called-default'
Error, verbatim
No error. This one succeeds.
CJS globals inside ESM
- ReferenceError: __dirname is not defined → import.meta.dirname
- ReferenceError: __filename is not defined → import.meta.filename
- ReferenceError: require is not defined → module.createRequire(import.meta.url)
- ReferenceError: module is not defined → no direct replacement
- ReferenceError: exports is not defined → no direct replacement
Every cell measured on Node v25.9.0 on 2026-08-07, with real packages.
Every cell above was run, not looked up. Three of them are worth reading closely.
Top-level await is the new failure
require() an ESM module whose graph contains a top-level await and you get:
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 is a good error. It names the cause, gives the fix, and hands you a flag that finds the offending module. It is also the message you will actually meet in 2026, which is why it is reproduced here verbatim rather than paraphrased.
Named imports from CommonJS depend on how the source is written
Node finds a CommonJS module’s named exports by analysing the source statically. An export whose key is computed at runtime is invisible:
const key = ['com', 'puted'].join('');
module.exports[key] = '…';
The namespace then contains only default and a literal module.exports key, and the
named import fails:
SyntaxError: Named export 'computed' not found. The requested module 'cjs-dynamic' is a
CommonJS module, which may not support all module.exports as named exports.
Note may not. This is not a rule about CommonJS; it is a statement about what a static analyser could see in that particular file. That is why the same import style works against one package and fails against another, which feels arbitrary until you know what it is looking at.
The dual package hazard is still here
A package that ships both entry points can end up loaded twice in one process:
require(pkg).Token === (await import(pkg)).Token → false
instance instanceof otherToken → false
Two classes, same source, different identities. Nothing throws. Your instanceof
check simply returns false, your error-type matching stops matching, and the symptom
appears somewhere far away from the cause.
This is the one that survived. The two errors everyone quotes got fixed because they were loud and blocked people immediately. This one is quiet, so it is still here.
The globals, for completeness
Inside ESM, all five CommonJS globals throw the same plain error:
ReferenceError: __dirname is not defined
…and likewise for __filename, require, module and exports. The replacements are
import.meta.dirname and import.meta.filename, both of which are strings, and
module.createRequire(import.meta.url) when you genuinely need require.
What to take from this
The useful habit is not memorising which combination works. It is noticing that this topic rots faster than almost anything else you will search for, and that a highly upvoted answer is evidence about the past.
Before you restructure a package because of something you read: run the two lines that reproduce it on the Node you actually ship. Half the time in 2026 the problem is already gone, and the change you were about to make would only add a layer of compatibility shim protecting you from nothing.
Common follow-ups
Is ERR_REQUIRE_ESM really gone?
For a plain ESM module, yes — measured on Node v25.9.0, require() of an ESM package succeeds. You get the module namespace, so the named exports are on the object you receive rather than under a default property. If the module graph contains top-level await it still fails, with a different error.
What does require() return for an ESM module?
The namespace object, with Symbol.toStringTag set to "Module" and an __esModule marker added. Code written for the old world that reaches for .default gets the default export as a property, which is usually not what it wanted.
Why do named imports from a CommonJS package sometimes fail?
Node detects CommonJS named exports by statically analysing the source, so an export whose key is computed at runtime is invisible to it. The error says the module "may not support all module.exports as named exports" — may not, because whether it works depends on how that module's source is written.
What is the dual package hazard?
A package that ships separate CommonJS and ESM entry points can be loaded twice in one process, once through each. The two copies do not share identity, so a class from one is not the same class as from the other and instanceof against it returns false. Nothing throws — the check just quietly says no.
What replaces __dirname in ESM?
import.meta.dirname, and import.meta.filename for __filename. Referencing __dirname, __filename, require, module or exports inside ESM throws a plain ReferenceError saying the name is not defined.