error TS18046: 'err' is of type 'unknown'. is reported when code reads a property of, or calls, a value typed unknown without narrowing it first. The everyday source is catch (err): under strict, useUnknownInCatchVariables types the caught value as unknown, because JavaScript can throw anything, not only Error objects.
The same check on an expression without a name, such as the result of a function whose @returns is unknown, is TS2571 (Object is of type 'unknown'.). Both say the same thing: prove what the value is before using it.
At a glance
| Code | TS18046 |
|---|---|
| Message | '{0}' is of type 'unknown'. |
Also on this page: TS2571 | Object is of type 'unknown'. |
| TypeScript's category | error |
| Listed under | Null, undefined and unknown |
| Turned on by | useUnknownInCatchVariables, strict |
| In .js files | Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file |
The messages are TypeScript's own text from its diagnostic messages file (Apache-2.0); {0} is filled in by tsc for each error.
What it looks like
/** @returns {unknown} */
function readSettings() {
return JSON.parse(localStorage.getItem("settings") ?? "{}");
}
export function loadPort() {
try {
return readSettings().port;error TS2571: Object is of type 'unknown'.
} catch (err) {
console.error(`Could not read settings: ${err.message}`);error TS18046: 'err' is of type 'unknown'.
return 3000;
}
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} What causes it
useUnknownInCatchVariablesis on (part ofstrict), socatch (err)giveserrthe typeunknown, and the code readserr.message.- A function returns
unknown(a JSON reader, a message handler), and its result is used directly; that is TS2571 when the result is not stored in a variable.
How to fix it
Narrow the value first
For errors, err instanceof Error ? err.message : String(err) covers both an Error and anything else that was thrown. For data, check the shape with typeof, in and !== null, or write a type guard function once and reuse it.
/** @returns {unknown} */
function readSettings() {
return JSON.parse(localStorage.getItem("settings") ?? "{}");
}
export function loadPort() {
try {
const settings = readSettings();
if (typeof settings === "object" && settings !== null && "port" in settings && typeof settings.port === "number") {
return settings.port;
}
return 3000;
} catch (err) {
console.error(`Could not read settings: ${err instanceof Error ? err.message : String(err)}`);
return 3000;
}
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Or annotate the catch clause, if you accept the risk
useUnknownInCatchVariables: false restores the older any for catch variables across the project, and catch (/** @type {any} */ err) does it for one clause. Both trade the check for convenience; the instanceof line is short enough to prefer.
Gotchas
instanceof Erroris false for errors from another realm (an iframe) and for libraries that throw plain objects; theString(err)fallback still prints something.unknownis the type-safe counterpart ofany: assigning anything to it is allowed, using it is not. The any and unknown page compares them.
Sources
- TSConfig Reference: useUnknownInCatchVariables · TypeScript
- More on Functions: unknown · TypeScript handbook
- diagnosticMessages.json (TypeScript 5.9.3) · TypeScript on GitHub
Last reviewed 20 October 2026.