Error [Named export not found]

Fixing "does not provide an export named" / "Named export not found" (Node.js)

FIX SUMMARY verified
Applies when
node:20-alpinenamed import from CommonJS

Verified: reproduced in node:20-alpine, then the Named export not found signature was gone after the fix (exit 0).

A named import from an ES module (ESM) can stop at either of these errors.

SyntaxError: The requested module './x.js' does not provide an export named 'foo'
SyntaxError: Named export 'foo' not found. The requested module './x.cjs' is a CommonJS module, which may not support all module.exports as named exports.

The first says “there is no export by that name”; the second says “the other side is CommonJS, and it cannot treat that name as a named export”. Unless the name is a typo, the cause is usually that you are named-importing a CommonJS module from ESM.

Why a named import from CommonJS can fail

A CommonJS module exports one object, module.exports. That is a different shape from ESM’s named exports. Even so, named import works most of the time, because Node analyses the CommonJS source statically and picks up assignments it can follow directly, such as module.exports.foo = ..., as named exports (the Node 20 / 22 documentation describes this analysis as cjs-module-lexer).

The problem is the ways of writing it that the analysis cannot pick up. Computing the key before assigning, for example, cannot be analysed.

// analysable (import { foo } usually works)
module.exports.foo = () => {};

// not analysable (import { foo } is "not found")
const name = 'foo';
module.exports[name] = () => {};

That is why one package can be named-imported while another CommonJS package fails. What decides it is not how you wrote your import; it is how the other package wrote its exports.

Reproduction (minimal)

Write a CommonJS module that exports in a form the static analysis cannot pick up.

// cjs-dep.cjs (.cjs is always CommonJS)
const name = 'greet';
module.exports[name] = function () {
  return 'hi from CJS';
};

Named-import it from ESM.

// index.js (package.json has "type": "module", so this is ESM)
import { greet } from './cjs-dep.cjs';
console.log(greet());

Running it prints Named export 'greet' not found ... and exits with code 1.

The fix: take the default import, then pull the name out of it

Receive the whole of module.exports through a default import and take what you need from it. The error message itself suggests this shape.

// index.js (default import, then destructure)
import pkg from './cjs-dep.cjs';
const { greet } = pkg;
console.log(greet());

This runs and exits with code 0. pkg is module.exports itself, so the value is read out of module.exports at run time rather than through the static analysis. If what you take out is undefined, check that the property really exists on the CommonJS side (and that the name is not a typo).

How it works: a named import is resolved before the module runs

ESM’s named import checks whether the name really exists before any code runs. When the other side is ESM, its export statements answer that. When the other side is CommonJS, there are no export statements, so Node infers the named exports by static analysis. A name it could not infer counts as absent, and the failure is a SyntaxError raised before execution.

A default import does no per-name check. It receives one module.exports object, so it can read a property however that property came to exist at run time.

If it still doesn’t work

  • It happens against your own ESM file: for does not provide an export named 'foo', check that the ESM really has an export, and that the name matches (case, and default vs named). export default pairs with import foo from ...; a named export pairs with import { foo } from ....
  • The default-imported pkg turned out to be the function itself: with CommonJS written as module.exports = function () {}, pkg is the function. Call pkg() rather than pkg.foo.
  • An ESM file said require is not defined: that is a different symptom. See require is not defined in ES module scope.
  • require()ing CommonJS produced a different error: if you were not reading CJS from ESM but require()ing an ES module from CJS, see require() of ES Module not supported (ERR_REQUIRE_ESM).

Verification (machine-verified)

This fix was reproduced inside the version-pinned node:20-alpine container, and the Named export not found signature was then machine-checked to be gone after the fix.

verify — run-case.mjs
$ node run-case.mjs node/named-export-not-found
● reproduce Named export 'greet' not found present ✓
● apply fix exit 0
● re-run Named export 'greet' not found gone ✓
PASS verified · node:20-alpine · signature gone

That check covers the image above and nothing else. If the fix did not work in your environment, or something here is wrong, send a report (the article and the verified image go into the subject and body).