TypeScript

Fix TS1084 Invalid 'reference' directive in @types/node

Why a types release from 2 September 2020 broke TypeScript 2.x builds overnight, why skipLibCheck does not help, and two fixes reproduced in Docker.

Mahdi Ben RhoumaMahdi Ben Rhouma
7 min read

TL;DR#

@types/node 6.14.11, published on 2 September 2020, added /// <reference lib="es2015" /> as line 20 of its index.d.ts. That directive arrived in TypeScript 3.0, so any TypeScript 2.x compiler stops on it with TS1084, even with skipLibCheck on. If you are stuck on TypeScript 2.x, pin the types with npm install --save-dev --save-exact @types/node@6.14.10. Otherwise, upgrade TypeScript to 3.x. Deleting the third slash by hand only lasts until the next npm install.

The error#

The Stack Overflow question behind this page was asked on 3 September 2020. The project declared "typescript": "^2.6.1" with "skipLibCheck": true, and compilation failed with:

text
node_modules/@types/node/index.d.ts(20,1): error TS1084: Invalid 'reference' directive syntax.

The same line came back in a clean container, in a minimal project of one file using import * as path from "path". The compiler was TypeScript 2.6.2, with @types/node 6.14.11, on Node 22 in the official node:22-alpine image (reproduced on 21 October 2026). The file that tsc points at is shown below:

text
--- node_modules/@types/node/index.d.ts lines 19-20:
// Reference required types from the default lib:
/// <reference lib="es2015" />

The question does not say which @types/node version was installed. But line 20 and es2015 (the directive quoted in the top answer) match the 6.x line from 6.14.11 onwards. A clean install of the ranges a project like that would have declared today gives the same failure:

text
### R1: typescript@^2.6.1 + @types/node@^6
installed: typescript@2.9.2 @types/node@6.14.13
node_modules/@types/node/index.d.ts(20,1): error TS1084: Invalid 'reference' directive syntax.
tsc exit code: 2

Why it happens#

The directive is newer than the compiler. The TypeScript 3.0 release notes introduce it in these words: "TypeScript adds a new triple-slash-reference directive (/// <reference lib="name" />), allowing a file to explicitly include an existing built-in lib file." A TypeScript 2.x parser knows /// <reference path> and /// <reference types>, but not lib, so it reports the line as invalid syntax. The message is a real diagnostic in the TypeScript source, code 1084, listed in the compiler's diagnostic messages for 2.6.2.

The types package changed under a caret range. In the npm registry metadata for @types/node, version 6.14.10 (April 2020) declares a minimum TypeScript of 2.8. Version 6.14.11 (2 September 2020) declares 3.1, and the later 6.x releases declare 3.2. Compare the two files on unpkg: 6.14.10/index.d.ts has no reference lib line, 6.14.11/index.d.ts has it at line 20. The same day's 7.10.12 release added /// <reference lib="es2016" />, also at line 20. With a range like ^6.x, the next fresh install picks up the new file without anyone touching package.json. That explains the timing: the question was asked the day after the release.

typesVersions cannot help a 2.x compiler. DefinitelyTyped packages ship separate folders per TypeScript version and select them through the typesVersions field. 6.14.11 has {"<=3.1":{"*":["ts3.1/*"]}}. That field arrived in the TypeScript 3.1 release notes. The publishing guide explains the fallback: when nothing in typesVersions matches, "TypeScript falls back to the types field". A TypeScript 2.x compiler never reads the field at all, so it always loads the root index.d.ts, the one with the directive.

Why skipLibCheck does not save you. The skipLibCheck reference describes it as "Skip type checking of declaration files." The question already had it set to true. In the reproduction, the error is identical with the option removed:

text
### case A2: same project, skipLibCheck removed
node_modules/@types/node/index.d.ts(20,1): error TS1084: Invalid 'reference' directive syntax.
tsc exit code: 2

That is the difference from the type errors in forcing tsc to ignore node_modules, where skipLibCheck is the right switch. Excluding node_modules in tsconfig.json does not help either: before TypeScript 6.0, every visible @types package is included in the compilation whether or not your code imports it, and exclude only filters what include matches.

Fix#

Pick one. Each fix below was applied to the failing project on its own and turned exit code 2 into exit code 0.

  1. Check which compiler and which types you really have. Run the project's own compiler, not a global one. If tsc is not even on your PATH, see running the local TypeScript compiler.
bash
npx tsc -v
npm ls typescript @types/node

If tsc -v prints a 2.x version and @types/node is 6.14.11 or later (or 7.10.12 or later on the 7.x line), you have this error.

  1. Staying on TypeScript 2.x: pin the types exactly. The -E flag matters. Per the npm install docs, --save-exact saves "an exact version rather than using npm's default semver range operator". Without it npm writes ^6.14.10, and the next install without a lockfile goes back to 6.14.13.
bash
npm install --save-dev --save-exact @types/node@6.14.10

In the reproduction this wrote "@types/node":"6.14.10" to package.json and survived a full reinstall:

text
--- rm -rf node_modules package-lock.json && npm install
installed: typescript@2.9.2 @types/node@6.14.10
tsc exit code: 0

If your code is on the 7.x types, the second answer on the question pinned 7.0.7. That also compiled against TypeScript 2.6.2 in the same test. If you do not need a specific Node major, DefinitelyTyped publishes per-compiler dist-tags (see the support window section of its README). @types/node@ts2.6 resolves to 12.12.6 today, and TypeScript 2.6.2 compiled against it too:

bash
npm install --save-dev --save-exact @types/node@ts2.6
  1. Or upgrade TypeScript. This is the longer-lived fix, because every @types package moves on. Against @types/node 6.14.13, TypeScript 3.0.3, 3.1.8 and 3.9.7 all compiled cleanly. The third answer on the question moved to ^3.9.7:
bash
npm install --save-dev typescript@3.9.7

If a framework CLI fixes which TypeScript versions it accepts, upgrade within that range or use the pin from step 2 instead.

Verify the fix#

Reinstall from scratch, so you are testing what CI will install and not a patched node_modules. Then compile:

bash
rm -rf node_modules
npm install
npx tsc -p .
echo "tsc exit code: $?"

A clean result prints nothing from tsc and tsc exit code: 0. Check the line tsc complained about as well. On a pinned 6.14.10 it is a comment, not a reference lib directive:

bash
sed -n '20p' node_modules/@types/node/index.d.ts

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

Deleting the third slash, and bumping the wrong package#

The hand edit is undone by the next install. The top-voted answer turns the directive into a plain comment (// <reference lib="es2015" />). That does work for the moment. In the reproduction, a fresh npm install restored the original file and the error with it:

text
### case H: case A project, third slash removed by hand (answer 1)
// <reference lib="es2015" />
tsc exit code: 0
--- after npm ci-style reinstall (rm -rf node_modules && npm install):
/// <reference lib="es2015" />
node_modules/@types/node/index.d.ts(20,1): error TS1084: Invalid 'reference' directive syntax.
tsc exit code: 2

Every CI run and every colleague's checkout starts from a clean install, so the build fails there even when it passes on the machine that was patched.

Upgrading @types/node instead of pinning it makes things worse. On TypeScript 2.6.2, the then-current @types/node 14.6.3 replaced the single TS1084 with a wall of parser errors. The new files use syntax the 2.x parser rejects:

text
node_modules/@types/node/assert.d.ts(2,68): error TS1144: '{' or ';' expected.
node_modules/@types/node/ts3.1/events.d.ts(21,46): error TS1005: '=' expected.
node_modules/@types/node/ts3.6/base.d.ts(10,1): error TS1084: Invalid 'reference' directive syntax.

Upgrading TypeScript only as far as 3.0.3 is not enough for that newer types release either. 3.0.3 accepts the reference lib directive, but it still failed on assert.d.ts with the same TS1144 errors. That is why step 3 above names a 3.x release that was tested against the types you actually install. A newer compiler needs the next generation of types in turn. On TypeScript 6 and later, types defaults to an empty list, which is how a missing "types": ["node"] turns into Cannot find name 'console'. Old types on a newer setup fail differently again: Cannot find name 'AbortSignal' in axios typings is what an ECMAScript-only lib with an old @types/node produces.

Whichever fix you choose, the lesson of this error is that a type package is a compiler input like any other. A caret range on @types/node with a frozen TypeScript is a build that can break on a day you changed nothing. Pin the types exactly while the compiler is old, or keep the compiler current so the types can move.

Further reading: Module resolves to an untyped module: fix TS2665 in .d.ts.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote