Know which error you caught
JavaScript has a small set of built-in Error subtypes, each thrown by specific
operations. Recognising the subtype tells you the class of bug instantly. This
reference lists every standard error type, what throws it, a typical message and
how to catch it with instanceof.
How it works
Every built-in error inherits from Error and carries name, message and a
stack. The engine throws the most specific subtype for the failure, which you
can branch on:
try {
null.foo; // TypeError: Cannot read properties of null
} catch (e) {
if (e instanceof TypeError) handleType(e);
else if (e instanceof RangeError) handleRange(e);
else throw e; // re-throw anything you do not handle
}
Because all subtypes extend Error, e instanceof Error is always true — order
your checks from most specific to least, and re-throw what you cannot handle.
Every built-in error type
| Error type | What throws it | Typical message |
|---|---|---|
TypeError | Wrong type for an operation | ”Cannot read properties of null”, “x is not a function” |
ReferenceError | Accessing an undeclared variable | ”x is not defined” |
RangeError | Numeric value out of allowed range | ”Invalid array length”, “Maximum call stack size exceeded” |
SyntaxError | Malformed code in runtime parse | ”Unexpected token”, “JSON.parse: unexpected character” |
URIError | Malformed URI in encodeURI/decodeURI | ”URI malformed” |
EvalError | Legacy — rarely thrown in modern engines | (legacy) |
AggregateError | Promise.any when all promises reject | Wraps an .errors array |
InternalError | Implementation limit hit (Firefox only) | “too much recursion” |
The most commonly encountered in practice are TypeError, ReferenceError, RangeError, and SyntaxError. EvalError is included for completeness but is not thrown by modern JavaScript engines in typical usage.
TypeError vs ReferenceError — the key distinction
These two are confused constantly:
- A
ReferenceErrormeans the identifier does not exist in the current scope. The variable was never declared.x is not defined— x has never been introduced withlet,const,var, or as a function parameter. - A
TypeErrormeans the identifier exists and has a value, but you are using it in a way that does not match its type.null.foo— null exists, but you cannot access properties on null.undefined()— undefined exists, but it is not callable.
If you get a ReferenceError, check your spelling and scope. If you get a TypeError, check what value the variable actually holds at runtime — it is almost always null, undefined, or a different type than expected.
AggregateError and Promise.any
AggregateError is thrown by Promise.any when every promise in the iterable rejects. It wraps all the rejection reasons in an errors array property:
try {
const result = await Promise.any([
Promise.reject(new Error("first")),
Promise.reject(new Error("second")),
]);
} catch (e) {
if (e instanceof AggregateError) {
console.log(e.errors); // [Error: first, Error: second]
}
}
Tips and notes
instanceofcan fail across realms (iframes, worker boundaries) where each has its ownErrorconstructor — checke.nameas a fallback in that case.SyntaxErrorfrom static source is uncatchable; only runtime-parsed code (JSON.parse,eval,new Function) throws a catchable one.- Use
Error.cause(new Error(msg, { cause })) to chain a lower-level error. - Custom errors should
extend Error, callsuper(message)and setthis.name. - A “Maximum call stack size exceeded” message is a
RangeError, usually from unbounded recursion. - Always re-throw errors you did not intentionally handle — swallowing an unexpected error silently is one of the hardest bugs to diagnose.