Error Handling
In JavaScript an error is any value that has been thrown — either by your code with the throw statement, or by the engine when something goes wrong. The conventional value is an instance of Error (or one of its subclasses), but technically you can throw any value at all. This page covers the Error class, its built-in subtypes, and the properties you can read off any thrown object.
The Error class
Error is a constructor on the global object. Calling it produces an object with a message, a name of "Error" and a captured stack. Throwing the result starts unwinding the call stack until something catches it.
const err = new Error("something went wrong");
console.log(err.name); // "Error"
console.log(err.message); // "something went wrong"
console.log(err.stack); // multiline trace (host-defined format)
throw err;The stack property is not part of the ECMAScript standard — every engine produces it, but the exact format varies. Treat it as diagnostic text, not as data you parse in production code.
Built-in error subtypes
The language defines a handful of Error subclasses for common failure modes. The engine throws specific subtypes, and you can throw them yourself when they match the situation.
TypeError— a value was the wrong type for the operation. The most common engine-thrown error:null.foo, calling a non-function, reassigning aconst.ReferenceError— referencing an identifier that has not been declared.RangeError— a numeric or length value is outside the allowed range:new Array(-1), recursion that exceeds the stack.SyntaxError— invalid source code. Mostly thrown at parse time; also fromJSON.parseon bad input.URIError— malformedencodeURI/decodeURIinput.EvalError— historical; rarely used in modern code.AggregateError— wraps multiple errors. Thrown byPromise.anywhen every input rejects.
try {
null.foo;
} catch (err) {
console.log(err.name); // "TypeError"
console.log(err instanceof TypeError); // true
}Common error properties
Every Error instance carries a few standard fields, plus some you can attach yourself:
name— the error class. Used byinstanceofand by tools that compare names across realms.message— the human-readable description passed to the constructor.stack— a string trace of where the error was thrown.cause— the original error this one wrapped, set vianew Error("...", { cause }).Anything else you assign to it (
err.code,err.statusCode,err.context = {...}).
Wrapping errors with cause
When you catch a low-level error and rethrow a higher-level one, attach the original as cause so it is preserved for debugging. Browsers and Node both show the chain in their default stack output.
async function loadConfig() {
try {
const response = await fetch("/config.json");
return await response.json();
} catch (original) {
throw new Error("could not load config", { cause: original });
}
}
loadConfig().catch((err) => {
console.error(err);
console.error("caused by:", err.cause);
});Branching on error type
Use instanceof in a single catch block to branch on the kind of error. Avoid string-matching on message — translations and engine differences will break it.
try {
doSomething();
} catch (err) {
if (err instanceof TypeError) {
console.log("bad type:", err.message);
} else if (err instanceof RangeError) {
console.log("out of range:", err.message);
} else {
throw err; // let unknown errors propagate
}
}Throwing non-Error values
throw accepts any value: throw "boom", throw 42, throw { code: 500 }. The engine never inspects what you throw, but tools and humans expect an Error.
Errors are values
You do not have to throw an error to use it — you can build one and pass it around like any other object. This shows up in callback APIs ((err, value) => ...) and in result-style error handling.
function parseAge(input) {
const n = Number(input);
if (!Number.isFinite(n)) return [new TypeError("not a number"), null];
if (n < 0) return [new RangeError("negative"), null];
return [null, n];
}
const [err, age] = parseAge("-3");
if (err) console.log(err.name, err.message); // "RangeError" "negative"