It makes the variable in a catch clause typed unknown instead of any. You must narrow it before reading properties. In a Node.js service that prevents code from assuming every thrown value is an Error, which is how crashes inside error handlers and leaked internals often start.
What the flag does
The TypeScript tsconfig reference describes it plainly: catch clause variables become unknown, so you narrow before use. Its example:
try {
riskyCall();
} catch (err) {
if (err instanceof Error) {
console.log(err.message);
}
}
It is one of the nine flags that strict turns on, so most strict projects already have it.
Why any was risky
JavaScript can throw anything: a string, a number, an object, or null. With any, the compiler lets you write err.message.toLowerCase() even when err is a string. The handler then throws a second error, often outside the code path your tests cover. Error handlers are code that runs when something is already wrong, so a bug there is expensive.
Safe handling in an HTTP service
- Narrow first. Use
instanceof Errorfor errors you expect. Use a small helper that converts anything into a safe message string. - Log the detail, return a generic message. Send the client a short fixed message and an id. Keep stack traces and internal messages in logs only.
- Do not echo the error back. Messages from databases, file systems and parsers can include paths, queries or values.
- Preserve the cause. When you wrap, pass the original so logs keep the chain.
- Rethrow what you cannot handle. Swallowing errors with an empty catch hides failures, including failed authorization checks.
A helper for step 1:
export function errorMessage(value: unknown): string {
if (value instanceof Error) return value.message;
if (typeof value === "string") return value;
return "Unknown error";
}
How this ties to Node behavior
Node.js treats an uncaught exception as a crash by default. The process docs say the event is a crude mechanism meant for last resort cleanup and that it is not safe to resume normal operation afterwards. Good catch blocks reduce how often you get there, but they do not replace a restart policy.
Adopting it
If you are not on strict yet, enable useUnknownInCatchVariables alone and fix the errors. They usually appear in a small number of files, and each fix is a few lines. See Is TypeScript strict mode a security setting for the rest of the group.
Limits
The flag only affects catch clauses. Promise rejections handled with .catch(handler) receive any in many type definitions, so narrow there too. A separate lint rule covers promises that nobody handles at all, which the post on floating promises explains.
Quick check
Is useUnknownInCatchVariables part of strict?
Yes. The TypeScript reference lists it among the nine flags enabled by strict.
Should I send error.message to the client?
Generally no. Internal messages can reveal paths, queries or values. Return a generic message and log the detail.
Sources
Last reviewed: 19 October 2026.