Skip to content
Latchkey

tsc TS18046: 'x' is of type 'unknown' in CI

TS18046 fires when you operate on a value typed unknown without narrowing it. Under strict mode, caught error variables are unknown, so reading err.message directly is an error.

What this error means

tsc fails with "error TS18046: 'err' is of type 'unknown'." inside a catch block or anywhere an unknown value is used before being narrowed.

tsc
src/run.ts:6:25 - error TS18046: 'err' is of type 'unknown'.

6   console.error('failed', err.message);
                            ~~~

Diagnose it: which tsconfig and which compiler?

A TypeScript error that appears only in CI usually means the runner is compiling with a different config or a different compiler version than your editor. Your editor uses the workspace TypeScript and the nearest tsconfig.json; CI uses whatever the lockfile resolved and whatever config the build script names.

Terminal
# what CI will actually use
npx tsc --version
npx tsc --showConfig | head -40

# which files are in the program (a missing include is a common cause)
npx tsc --listFiles | wc -l

# type-check only, no emit, same as most CI gates
npx tsc --noEmit

Common causes

A catch variable is unknown under strict mode

useUnknownInCatchVariables (on under strict) types caught errors as unknown, so accessing properties without a guard is an error.

An unknown value used without narrowing

Values from JSON.parse, generic boundaries, or APIs typed unknown must be narrowed before use.

How to fix it

Narrow the value before using it

  1. Check the value with instanceof or a type guard.
  2. Access properties only inside the narrowed branch.
  3. Re-run tsc to confirm the unknown is handled.
src/run.ts
try {
  run();
} catch (err) {
  const message = err instanceof Error ? err.message : String(err);
  console.error('failed', message);
}

Use a typed helper to extract a message

Centralize narrowing in a helper so catch blocks stay clean and consistent.

src/errors.ts
function toMessage(e: unknown): string {
  return e instanceof Error ? e.message : String(e);
}

Pin the compiler so unrelated updates cannot break the gate

TypeScript adds errors in minor releases. An unpinned compiler turns a routine dependency update into a red build on code nobody touched, which is the most common false alarm in a TypeScript CI pipeline.

package.json
// package.json
{
  "devDependencies": {
    "typescript": "5.6.3"   // exact, not ^5.6.3
  }
}

How to prevent it

  • Narrow caught errors with instanceof Error before use.
  • Treat unknown from parsing and APIs as needing a guard.
  • Keep strict (and useUnknownInCatchVariables) consistent across configs.

Frequently asked questions

What causes tsc TS18046: 'x' is of type 'unknown' in CI?
There are 2 common causes: a catch variable is unknown under strict mode and an unknown value used without narrowing. useUnknownInCatchVariables (on under strict) types caught errors as unknown, so accessing properties without a guard is an error.
How do I fix tsc TS18046: 'x' is of type 'unknown' in CI?
There are 2 fixes depending on which cause you have: narrow the value before using it and use a typed helper to extract a message. Work through them in order, since the first is the most common.
What does tsc TS18046: 'x' is of type 'unknown' in CI actually mean?
tsc fails with "error TS18046: 'err' is of type 'unknown'." inside a catch block or anywhere an unknown value is used before being narrowed.
How do I stop tsc TS18046: 'x' is of type 'unknown' in CI happening again?
Narrow caught errors with instanceof Error before use. The prevention section lists 3 changes that keep it from recurring.

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