Skip to content
Nilay Kabariya

Entry No.013·Fixes··3 min read

Fix: Unknown file extension ".ts" (ERR_UNKNOWN_FILE_EXTENSION)

Node refuses to run your TypeScript file. The version where this stops happening, what ts-node has to do with it, and the one-line way to run .ts today.

The error, verbatim

Jump to the fix ↓
node:internal/modules/esm/get_format:189
  throw new ERR_UNKNOWN_FILE_EXTENSION(ext, filepath);
        ^

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for C:\project\src\index.ts
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:189:9)
    at defaultGetFormat (node:internal/modules/esm/get_format:232:36)
    at defaultLoad (node:internal/modules/esm/load:145:22) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
✓ Reproduced on Windows 11 Pro

Tested on

Node
20.19.4, 22.5.1, 22.6.0, 22.18.0, 24.14.0
ts-node
10.9.2
tsx
4.23.15
TypeScript
5.9.3 and 7.0.2
OS
Windows 11 Pro
Contents
  1. The fix01
  2. Which Node version does what02
  3. Why it happens03
  4. Why ts-node hits it too04
  5. What didn’t work05
  6. The trap after the fix06
  7. Test runners07
  8. Run it yourself08
  9. How this was tested09

The fix

Run your Node version first, because this error disappears on its own from Node 22.18:

node --version

On Node 22.18 or newer (including Node 24), run the file directly. No loader, no flag, no ts-node:

node src/index.ts

On anything older, install tsx and run the file through it:

npm i -D tsx
npx tsx src/index.ts

For an npm script, or where you need Node’s own flags, load tsx into Node instead:

{ "scripts": { "dev": "node --import tsx src/index.ts" } }

Which Node version does what

I ran the same file, unchanged, on five Node versions:

Node node src/index.ts With --experimental-strip-types
20.19.4 ERR_UNKNOWN_FILE_EXTENSION flag doesn’t exist
22.5.1 ERR_UNKNOWN_FILE_EXTENSION bad option
22.6.0 ERR_UNKNOWN_FILE_EXTENSION runs
22.18.0 runs runs
24.14.0 runs runs

So there are three eras, and the answer you find online depends entirely on which one it was written in: before 22.6 Node could not load a .ts file at all, from 22.6 it could behind a flag, and from 22.18 it does it by default.

Why it happens

Node decides how to load a file from its extension. In an ES module project, the list is short: .js, .mjs, .cjs, .json, .node and, on new enough versions, .ts, .mts and .cts. Anything else is refused by name before a single line is read, which is why the stack trace points at get_format and never at your code.

Newer Node versions add TypeScript by stripping the types: the type annotations are replaced with whitespace and the result is run as plain JavaScript. Nothing is type-checked and nothing is compiled, which is why it’s fast and also why it can’t handle everything (see the trap below).

Why ts-node hits it too

This is the version most people actually have, because the error arrives from a tool that is supposed to prevent it:

npx ts-node src/index.ts
TypeError: Unknown file extension ".ts" for C:\project\src\index.ts
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:189:9)
  code: 'ERR_UNKNOWN_FILE_EXTENSION'

ts-node hooks into CommonJS loading. The moment your package.json has "type": "module", your .ts file is an ES module, a different loader handles it, and ts-node’s hook is never consulted. Node reaches the extension check and refuses.

Proof it’s the module system and not the file: I removed "type": "module" from package.json and ran the identical command on the identical Node 20, and it printed hello world.

What didn’t work

The trap after the fix

Type stripping is not compilation. Syntax that has to generate JavaScript, like enum and namespace, is refused:

SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode

That’s Node 24 running a file whose only crime is one enum. The ways past it, all five variants reproduced: TypeScript enum is not supported in strip-only mode.

Test runners

  • node --test follows the same rule as node itself: on Node 20 the .ts test file fails, on Node 24 it runs.
  • Mocha 12 strips types itself, so a .ts test ran on Node 20 as well, but the enum file still failed there with ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX.

Run it yourself

The project I used is on GitHub, so you can trigger the error on your own machine in under a minute:

git clone https://github.com/nils44344/node-ts-error-repros
cd node-ts-error-repros/01-unknown-file-extension
npm install && npm run repro

How this was tested

A three-line TypeScript file and a package.json with "type": "module", run unchanged on five Node versions installed side by side, with ts-node 10.9.2, tsx 4.23.15, Mocha 12.0.2, and TypeScript 5.9.3 and 7.0.2. Every result above is the actual terminal output, including the failures.

— N.K., end of entry No.013

Useful? Pass it on:Post on XFollow @EmotionalMatter

Related entries

  1. No.025

    Fix: TypeScript enum is not supported in strip-only mode

    Node's ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX for enum, parameter properties, namespace, export = and import =. Two ways to run it unchanged, both tested.

    > enum Color { Red, Green }

    FIXED3 min
  2. No.015

    Fix: This syntax is not allowed when 'erasableSyntaxOnly' is enabled

    TS1294: TypeScript rejects your enum, namespace or constructor shorthand. Why new projects turn this on, what each piece becomes, and when to switch it off.

    > src/a.ts(1,6): error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.

    FIXED3 min
  3. No.014

    Fix: ERR_MODULE_NOT_FOUND Cannot find module in ES modules

    Node can't find a file that is clearly there. What ES modules changed about relative imports, why TypeScript path aliases break at runtime, and what to change.

    > node:internal/modules/esm/resolve:275

    FIXED2 min

Post card · Newsletter

Get the next fix in your inbox.

One short email when a new entry is published. No spam, never shared, and you can leave any time.

— Nilay

or follow by RSSor on X

By subscribing you agree to the privacy note. One click to leave.

tip: paste the exact error text