A program that fails silently costs more than a program that stops. But the failure still has to carry enough to understand it: what broke, where, and why.
That is the job of the Error object, the only object in the language that captures the exact spot where it was created.
Definition
An Error is an object describing a failure. It carries three properties: name, the kind of error, message, the readable description, and stack, the run of calls that led there.
function divide(a, b) {
if (b === 0) throw new Error("Division by zero");
return a / b;
}
try {
divide(10, 0);
} catch (err) {
console.log(err.name, "|", err.message); // Error | Division by zero
} finally {
console.log("always runs");
}throw stops the function there and then and travels up level by level to the first try it meets. With no catch anywhere, execution halts and the message goes to the console.
The native types
The language throws typed errors of its own. Recognizing the name saves a great deal of time.
| Type | When it shows up |
|---|---|
TypeError | A value is not shaped as expected |
ReferenceError | A name in use exists nowhere |
SyntaxError | The code or the JSON is malformed |
RangeError | A value falls outside the allowed bounds |
All of them inherit from Error, so err instanceof Error is true for each. Testing instanceof against the precise type is what lets one case be handled on its own.
Defining your own error type
A class that extends Error is enough to create a recognizable type, to which you add whatever fields the handling needs.
class PaymentError extends Error {
constructor(message, code) {
super(message);
this.name = "PaymentError";
this.code = code;
}
}
try {
throw new PaymentError("Card declined", "card_declined");
} catch (err) {
if (err instanceof PaymentError) console.log(err.code); // card_declined
}The second argument of new Error keeps the original failure: new Error("Unreadable configuration", { cause: lowLevelError }). The cause field removes the choice between a meaningful message and the technical trace, since both now travel together.
Frequently asked questions
Can something other than an Error be thrown?
The language allows it, a string or an object go through perfectly well. It is still to be avoided: the thrown value will have no name and no stack, and the code catching it will not know where it came from. A thrown string loses most of the diagnosis.
What exactly does stack hold?
The run of calls that led to the object being created, as text. Its format is not standardized and varies between engines: use it for logs and debugging, never as data to be parsed.
How do you separate an expected failure from a real defect?
By defining a type for the first one. An empty cart or a declined card belong to the normal path and get recovered from, while a TypeError signals a defect to fix. That split shapes failure handling on the server, a subject covered in the Node.js course.