Skip to content
Latchkey

Jest "SyntaxError: Cannot use import" - Fix --experimental-vm-modules in CI

Jest’s native ESM support is gated behind Node’s --experimental-vm-modules flag. Without it, Jest loads ESM test files through its CommonJS path and they fail to parse import syntax.

What this error means

Jest tests using import (or testing ESM-only packages) fail with a SyntaxError about import, or Jest warns that ESM support needs the experimental flag. The same tests pass once the flag is set.

Jest output
SyntaxError: Cannot use import statement outside a module
# or
Jest encountered an unexpected token ...
To enable ESM support, run node with --experimental-vm-modules

Diagnose it: what is different about the runner?

A build that passes locally and fails on a runner differs in a small number of predictable ways. Check those before changing build configuration, because the build config is usually not the thing that changed.

.github/workflows/ci.yml
- run: |
    node --version && npm --version
    echo "NODE_ENV=$NODE_ENV  CI=$CI"
    nproc && free -h && df -h /
    ls -la node_modules/.bin | head

Common causes

Jest run without the ESM flag

Native ESM in Jest requires Node’s --experimental-vm-modules. Without it, Jest cannot evaluate ES modules and import syntax fails.

ESM-only dependencies under test

Even with mostly-CJS tests, importing an ESM-only package pulls in the ESM path, which needs the flag.

How to fix it

Pass the flag via NODE_OPTIONS

Enable Node’s experimental VM modules when invoking Jest.

package.json / Terminal
# package.json script
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
# or in CI:
NODE_OPTIONS=--experimental-vm-modules npx jest

Or transform ESM to CJS

  1. Configure transform (babel-jest/ts-jest) to compile ESM/TS down to CommonJS for Jest.
  2. Add ESM-only deps to transformIgnorePatterns exceptions so they get transformed.
  3. Pick one approach - native ESM (flag) or transform - not both.

The three that account for most of them

  • Case sensitivity. Linux runners are case sensitive, macOS is not. An import with the wrong case resolves locally and fails in CI.
  • Out of memory. Exit code 137 is a SIGKILL from the kernel, not a build error. Raise --max-old-space-size or use a larger runner.
  • devDependencies pruned. NODE_ENV=production makes npm ci skip devDependencies, so the build tool itself goes missing. Set it after install, not before.

How to prevent it

  • Set NODE_OPTIONS=--experimental-vm-modules for ESM Jest runs.
  • Or standardize on a CJS transform for tests.
  • Keep the test module strategy consistent across the repo.

Frequently asked questions

What causes Jest "SyntaxError: cannot use import"?
There are 2 common causes: jest run without the esm flag and esm-only dependencies under test. Native ESM in Jest requires Node’s --experimental-vm-modules.
How do I fix Jest "SyntaxError: cannot use import"?
There are 2 fixes depending on which cause you have: pass the flag via node_options and or transform esm to cjs. Work through them in order, since the first is the most common.
What does Jest "SyntaxError: cannot use import" actually mean?
Jest tests using import (or testing ESM-only packages) fail with a SyntaxError about import, or Jest warns that ESM support needs the experimental flag.
How do I stop Jest "SyntaxError: cannot use import" happening again?
Set NODE_OPTIONS=--experimental-vm-modules for ESM Jest runs. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card