Node "Dynamic require of X is not supported" in Bundled ESM in CI - Fix the Bundle
By Daniel Zoghalchali·Latchkey
When a bundler emits ESM, runtime require() is not available, so any require call that survives bundling throws "Dynamic require of X is not supported".
What this error means
A bundled output run in CI throws Error: Dynamic require of "<name>" is not supported, from a dependency that still calls require() at runtime under the ESM bundle.
node
Error: Dynamic require of "fs" is not supported
at file:///home/runner/work/app/app/dist/index.mjs:12:9
at node_modules/some-cjs-dep/index.js
Diagnose it: the shell in CI is not your shell
Package scripts run under a different shell, a different PATH, and a non-interactive environment on a runner. Most scripts that fail only in CI are relying on something the login shell gave them locally: a tool on PATH, an environment variable from a dotfile, or a TTY.
Terminal
# what the script can actually see
npm run env | grep -E "^(PATH|NODE_ENV|CI)="
# is the binary on PATH for the script, not just for you?
npm exec -- which <tool> || echo "not resolvable from npm scripts"
# run the exact script with tracing
sh -x -c "$(node -p "require('./package.json').scripts.build")"
Common causes
A CommonJS dependency using runtime require in an ESM bundle
The bundler produced ESM, but a bundled CommonJS module calls require() at runtime, which the ESM output cannot satisfy.
Conditional or dynamic require inlined into the bundle
A require behind a condition was inlined instead of externalized, so it runs in the ESM context.
How to fix it
Mark the dependency external
Keep the CommonJS dependency external so it loads from node_modules at runtime.
Ship node_modules alongside the bundle.
esbuild config
// esbuild
external: ['some-cjs-dep']
Target a CommonJS bundle format
Bundle to CommonJS so runtime require is available.
A multi-command script can report success while a middle command failed, which produces the worst kind of CI result: a green build that shipped something broken.
.github/workflows/ci.yml
# pipefail is NOT set by default in every runner shell- name:Buildshell:bashrun:|set -euo pipefailnpm run build | tee build.log
How to prevent it
Choose the bundle format that matches how dependencies load, externalize Node built-ins and CommonJS-only packages, and test the bundled artifact in CI before relying on it.
Frequently asked questions
What causes Node "Dynamic require of X is not supported" in bundled ESM in CI?
There are 2 common causes: a commonjs dependency using runtime require in an esm bundle and conditional or dynamic require inlined into the bundle. The bundler produced ESM, but a bundled CommonJS module calls require() at runtime, which the ESM output cannot satisfy.
How do I fix Node "Dynamic require of X is not supported" in bundled ESM in CI?
There are 2 fixes depending on which cause you have: mark the dependency external and target a commonjs bundle format. Work through them in order, since the first is the most common.
What does Node "Dynamic require of X is not supported" in bundled ESM in CI actually mean?
A bundled output run in CI throws Error: Dynamic require of "<name>" is not supported, from a dependency that still calls require() at runtime under the ESM bundle.
How do I stop Node "Dynamic require of X is not supported" in bundled ESM in CI happening again?
Choose the bundle format that matches how dependencies load, externalize Node built-ins and CommonJS-only packages, and test the bundled artifact in CI before relying on it.