ts-node "register" Errors - Fix ESM vs CJS Registration in CI
ts-node has two registration paths: a CommonJS require/-r ts-node/register hook, and an ESM loader. Using the CJS hook for an ESM project (or vice versa) produces import syntax errors or unknown-extension failures because the wrong transformer is active.
What this error means
Running TypeScript via ts-node fails with Cannot use import statement outside a module, ERR_UNKNOWN_FILE_EXTENSION, or an ESM loader error. The registration mode (CJS register vs ESM loader) does not match the project’s "type" and tsconfig module setting.
# ESM project registered with the CJS hook:
SyntaxError: Cannot use import statement outside a module
# or CJS project run through the ESM loader:
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts"Common causes
CJS register used for an ESM project
A package with "type": "module" (or ESM tsconfig) run with -r ts-node/register uses the CommonJS hook, which cannot handle import syntax, so it fails to parse.
ESM loader used for a CJS project, or version skew
Pointing the ESM loader at a CommonJS setup - or mismatching ts-node and TypeScript/Node versions - leaves files unhandled, yielding unknown-extension errors.
How to fix it
Match the registration to the module system
Use the ESM loader for ESM projects and the register hook for CJS.
# CommonJS project
node -r ts-node/register src/index.ts
# ESM project ("type":"module")
node --loader ts-node/esm src/index.ts # older Node
# or the current register-based form ts-node documentsAlign versions and config
- Keep ts-node, TypeScript, and Node versions compatible.
- Set tsconfig
module/moduleResolutionto match the chosen runtime (CJS vs NodeNext/ESM). - Consider tsx as a simpler single-mode alternative if the dual-mode setup is fragile.
How to prevent it
- Use one registration mode that matches the project’s module system.
- Keep ts-node/TypeScript/Node versions aligned.
- Make tsconfig module settings consistent with package "type".