Fix Babel "native ECMAScript module configuration file"
Get babel-jest loading your Babel config again in a "type": "module" project, and see which Node versions never hit the error at all.
TL;DR#
Your package.json says "type": "module", so Node treats babel.config.js as an ES module. In the failing Jest path, babel-jest loads the Babel configuration synchronously, through require(). Node 20 before 20.19 and Node 22 before 22.12 refuse to require() an ES module without a flag (Node 18, and Node 20 before 20.17, cannot do it at all), so Babel gives up with this error. Rename the file to babel.config.cjs and write it with module.exports (or use babel.config.json), or run your tests on a compatible supported Node release where require() of ES modules is enabled by default.
The error#
This is how the error reads in the original Stack Overflow question, from a npx jest audit running in a GitHub workflow:
Test suite failed to run
/Users/frankukachukwu/StudioProjects/covid-19-estimator-tksilicon-js/babel.config.js: Error while loading config - You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously.Babel 7.29.7 prints a longer message without the "Error while loading config" prefix, and names the file on its own line. This is the output reproduced on 7 October 2026 in an official Node Alpine container, with @babel/core 7.29.7, babel-jest 30.5.2 and Jest 30.5.2 on Node 18.20.8. The version comparisons below are those recorded tests, not a claim that these are the latest packages:
FAIL ./sum.test.js
● Test suite failed to run
You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously or when using the Node.js `--experimental-require-module` flag.
at /app/babel.config.js
at loadPartialConfigSync (node_modules/@babel/core/src/config/index.ts:53:60)The project behind it is the smallest one that triggers the error: a package.json with "type": "module", one test file using import, and this config:
export default {
presets: [["@babel/preset-env", { targets: { node: "current" } }]],
};Why Babel refuses the config#
Two facts meet here.
Under "type": "module", a .js config is an ES module. The Babel config files documentation ties the interpretation of babel.config.js to the package type: module packages use the ESM form, while CommonJS packages use the CommonJS form. The explicit .mjs and .cjs extensions remove that ambiguity.
This babel-jest path asks for the config synchronously. The trace above ends in loadPartialConfigSync, and babel-jest's source calls loadPartialConfigSync on its synchronous transform path. The source also has an asynchronous path; the error identifies which one this run used. Asynchronous import() cannot supply an immediate result to a synchronous loader.
Babel's loader, in module-types.ts, handles this by trying require() first. When require() throws because the file is an ES module and Babel is running synchronously, it has no other route and raises the error you see. The message's mention of --experimental-require-module is that same require() route: it works whenever the running Node can require() an ES module.
That is why the Node version decides the outcome in this fixture. The release notes for Node 22.12.0 and Node 20.19.0 record when loading ES modules with require() became enabled by default on those release lines. The Node.js modules documentation limits synchronous loading to modules without top-level await, including their dependencies. The same failing project, unchanged, was run on several official Node Alpine images:
| Node | Result without any flag |
|---|---|
| 18.20.8 | fails with this error |
| 20.17.0 | fails with this error |
| 20.18.3 | fails with this error |
| 20.19.0 | passes |
| 22.11.0 | fails with this error |
| 22.12.0 | passes |
| 22.23.3 | passes |
On 22.12.0, with nothing changed but the image:
v22.12.0
PASS ./sum.test.js
✓ adds (6 ms)A CI job that pins 22.11 fails while a laptop on a later 22.x passes. When the error only appears in one environment, compare node --version there first.
Babel's own docs still say that .mjs config files "will throw when calling Babel synchronously". On Node 22.23.3 with @babel/core 7.29.7, the project passes with the config renamed to babel.config.mjs as well, so that sentence describes Node releases that cannot require() an ES module.
Fix#
Pick one. Each was reproduced on its own, starting from the failing project.
-
Run the tests on a compatible supported Node release with synchronous ESM loading. The tested thresholds are Node 20.19.0 and 22.12.0; use a maintained release, not an old patch merely because it was in the experiment. For a
.jsconfig, Node 22.12.0 loaded @babel/core 7.25.2, 7.25.7 and 7.29.7 (with 7.11.0 the config loads too, but this Jest 30 setup then demands @babel/core 7.22 or later, see below). In a GitHub Actions workflow such as the one in the question:yaml- uses: actions/setup-node@v4 with: node-version: 22 - name: Run Audits run: npx jest audits/ch-2 --json --outputFile=audits/ch-2.json --noStackTrace -
Rename the config to
babel.config.cjsand switch it to CommonJS. This is the accepted answer. It avoids requiring a native ES module, while the Node release must still be compatible with your installed Babel, Jest and presets:jsmodule.exports = { presets: [["@babel/preset-env", { targets: { node: "current" } }]], };The renamed file passes on Node 18.20.8:
textbabel.config.cjs module.exports = { presets: [["@babel/preset-env", { targets: { node: "current" } }]], }; v18.20.8 PASS ./sum.test.jsThe accepted answer also adds a
jest.config.cjs. The reproduction had no Jest config file at all, and renaming the Babel config alone was enough. -
Or use
babel.config.json. A JSON config has no module system, so the question never arises. This was the second answer on the question, and it passes on Node 18.20.8 as well:json{ "presets": [["@babel/preset-env", { "targets": { "node": "current" } }]] }Babel's docs add a reason to prefer it: a static JSON file can be cached safely by other tools. You lose the ability to compute options in JavaScript.
-
Stuck on Node 20.17.0 or 20.18.x? Pass the flag the message names:
bashNODE_OPTIONS=--experimental-require-module npx jestOn both 20.17.0 and 20.18.3 this turns the failure into a pass, with Node printing a warning that the feature is experimental:
textv20.18.3 (node:8) ExperimentalWarning: Support for loading ES Module in require() is an experimental feature and might change at any time (Use `node --trace-warnings ...` to show where the warning was created) (node:20) ExperimentalWarning: Support for loading ES Module in require() is an experimental feature and might change at any time (Use `node --trace-warnings ...` to show where the warning was created) PASS ./sum.test.jsNode 18 rejects the flag outright (
node: bad option: --experimental-require-module), so there the choice is between options 1, 2 and 3.
Verify the fix#
Run the suite again with the same command your CI uses. The suite should reach your tests: PASS or a real assertion failure, not Test suite failed to run. Then check that only one config file is left with ls babel.config.*. Copying instead of renaming leaves the old file in place, and Babel does not quietly pick the good one. On Node 18 the leftover babel.config.js still raises the original error. On Node 22.23.3, Babel refuses to choose:
Multiple configuration files found. Please remove one:
- babel.config.js
- /app/babel.config.cjs
from /appOne email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
When the old Babel is the next problem#
The question's message, with its "Error while loading config" prefix, comes from an older @babel/core. Reproduced with @babel/core 7.11.0 on Node 18.20.8:
/app/babel.config.js: Error while loading config - You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously.Moving that same project to Node 22.12.0 gets past the config file, and Jest 30 then stops on the Babel version itself:
[BABEL] /app/sum.test.js: Requires Babel "^7.22.0 || ^8.0.0-0", but was loaded with "7.11.0". If you are sure you have a compatible version of @babel/core, it is likely that something in your build process is loading the wrong version. Inspect the stack trace of this error to look for the first entry that doesn't mention "@babel/core" or "babel-core" to see what is calling Babel. (While processing: "/app/node_modules/@babel/plugin-syntax-import-attributes/lib/index.js")In that reproduced dependency combination, a newer Node alone only trades one failure for another. If the next failure names an incompatible Babel version, align @babel/core with the requirements of the actual presets and plugins in the trace.
Changes that look like fixes but are not#
These were each run as negative controls, and all three still fail:
-
Renaming without rewriting. A
babel.config.cjsthat still saysexport defaultis parsed as CommonJS and fails with a syntax error instead:text/app/babel.config.cjs:1 export default { ^^^^^^ SyntaxError: Unexpected token 'export' -
Renaming to
.mjs. That only makes the ES module explicit. On Node 18 the error is the same, now pointing at/app/babel.config.mjs. The same.mjsfile passes on Node 22.23.3, so the extension was never the problem; the Node release was. -
Adding
--experimental-vm-modules. SettingNODE_OPTIONS=--experimental-vm-moduleson Node 18, the flag Jest's ESM support relies on, still fails, and the trace still ends inloadPartialConfigSync. If you do move your tests to native ESM, thejestglobal goes away too: see ReferenceError: jest is not defined in ESM tests.
When the message mentions top-level await instead#
Upgrading Node does not cover every ESM config. A config that uses top-level await, for example to load a preset dynamically, cannot be loaded by require() on any Node version. On Node 22.23.3, the error changes to:
You appear to be using a configuration file that contains top-level await, which is only supported when running Babel asynchronously.
at /app/babel.config.jsMove the asynchronous work out of the config, or write the config as .cjs and keep it synchronous: this Jest path asks for the config through the same synchronous call, whatever the file contains. Renaming a file does not make asynchronous dependencies synchronous.
This error belongs to a family of Node ES module and CommonJS boundaries. The same require() versus import split explains ERR_PACKAGE_PATH_NOT_EXPORTED when a package exposes a subpath only under the "import" condition, and JSON files need their own import attribute under native ESM, as covered in ERR_IMPORT_ASSERTION_TYPE_MISSING. For this error, the config must be loadable synchronously on the Node release in use. A valid synchronous CommonJS config or a static JSON config avoids the ESM-loading requirement; incompatible dependencies can still produce a separate error.
Further reading: Fix Jest Cannot find module on absolute path imports.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix Jest Cannot find module on absolute path imports
Why an import that already matches moduleDirectories still throws Cannot find module: a failing test, then a passing one, for two different fixes.
Cannot use 'import.meta' outside a module: Jest + Vite fix
Three fixes for import.meta.env in Jest tests of a Vite app, each reproduced, plus the secret-leak trap in the popular process.env answer.
Fix: Directory import is not supported resolving ES modules
Why Node throws ERR_UNSUPPORTED_DIR_IMPORT under "type": "module", three fixes reproduced on Node 24, and why the old specifier flag no longer helps.


