Skip to content
Latchkey

Node ERR_UNSUPPORTED_DIR_IMPORT in CI - Import a File, Not a Directory

ERR_UNSUPPORTED_DIR_IMPORT means native ESM was asked to import a directory. Unlike CommonJS, ESM does not auto-resolve a folder to its index file.

What this error means

A program run as ESM throws Error [ERR_UNSUPPORTED_DIR_IMPORT] for an import that points at a directory rather than a concrete file.

node
node:internal/modules/esm/resolve:217
Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import
'/home/runner/work/app/app/src/utils' is not supported resolving ES modules
    at finalizeResolution (node:internal/modules/esm/resolve:217:11)

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

Importing a folder under native ESM

ESM does not append /index.js to a directory specifier, so a directory import fails.

A TypeScript directory import not rewritten on emit

Source imports a folder, and the compiler emits the same directory specifier into ESM output.

How to fix it

Import the explicit index file

  1. Change the directory import to point at the concrete file.
  2. Include the .js extension.
JavaScript
import { fn } from './utils/index.js';

Make failures fail the job

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: Build
  shell: bash
  run: |
    set -euo pipefail
    npm run build | tee build.log

How to prevent it

  • Write explicit file paths with extensions in ESM source, avoid directory imports, and configure the compiler to emit resolvable specifiers.

Frequently asked questions

What causes Node ERR_UNSUPPORTED_DIR_IMPORT in CI?
There are 2 common causes: importing a folder under native esm and a typescript directory import not rewritten on emit. ESM does not append /index.js to a directory specifier, so a directory import fails.
How do I fix Node ERR_UNSUPPORTED_DIR_IMPORT in CI?
Import the explicit index file. Change the directory import to point at the concrete file.
What does Node ERR_UNSUPPORTED_DIR_IMPORT in CI actually mean?
A program run as ESM throws Error [ERR_UNSUPPORTED_DIR_IMPORT] for an import that points at a directory rather than a concrete file.
How do I stop Node ERR_UNSUPPORTED_DIR_IMPORT in CI happening again?
Write explicit file paths with extensions in ESM source, avoid directory imports, and configure the compiler to emit resolvable specifiers.

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