JS Error Object
In this page:
The name, message, and stack Properties
Every Error object carries three key properties: .name identifies the error type (like TypeError), .message holds the human-readable description you (or the engine) provided, and .stack contains a trace of the function calls that led to where the error was created, invaluable for tracking down exactly where something went wrong.
Note: Log err.stack (not just err.message) during debugging whenever you need to know exactly where in the code an error actually occurred, not just what it says.
Warning: The exact format of .stack is not standardized across JavaScript engines -- it is extremely useful for human debugging, but should not be parsed programmatically to extract structured information.
Example: The name, message, and stack Properties
try {
null.name;
} catch (e) {
console.log(e.name, e.message);
console.log(e.stack);
}
Throwing Non-Error Values
JavaScript's throw statement accepts any value at all -- a string, a number, a plain object -- not just an Error instance, but doing so loses the standard name/message/stack structure, making the thrown value harder to work with consistently in a catch block that expects a real Error.
Note: Always throw a real Error (or a subclass of it) rather than a bare string or plain object, so every catch block in your codebase can rely on the same consistent shape.
Warning: Catching code that assumes err.message always exists will fail (or produce undefined) if the thrown value was a bare string or a plain object instead of a real Error.
Example: Throwing Non-Error Values
try {
throw "just a string"; // not an Error instance
} catch (e) {
console.log(typeof e, e); // no .name/.message/.stack structure
}
Adding Custom Properties to an Error
Beyond the standard three properties, you can attach additional custom properties directly to an Error instance after creating it -- like a specific error code, an HTTP status, or contextual data relevant to what failed -- extending the base Error object without needing a full custom subclass.
Note: Attach a small number of clearly-named custom properties (like .code or .statusCode) to an error when a full custom Error subclass feels like overkill for the situation.
Warning: Attaching many ad-hoc custom properties inconsistently across different parts of a codebase can make errors harder to handle predictably -- consider a proper custom Error class once the pattern becomes common.
Example: Adding Custom Properties to an Error
const err = new Error("Something failed");
err.code = "E_CUSTOM";
err.statusCode = 500;
console.log(err.code, err.statusCode, err.message);
The cause Property
A more recent addition, the cause option (passed as the second argument to Error's constructor: new Error(message, { cause: originalError })) lets you chain errors together, preserving the original underlying error while wrapping it in a more specific, higher-level one.
Note: Use the cause option when catching a low-level error and re-throwing a more descriptive, higher-level one, so the original underlying cause is not lost in the process.
Warning: The cause property is a relatively recent addition (ES2022) -- while supported in all current major browsers, be aware it may not exist in a very old browser environment.
Example: The cause Property
try {
try {
throw new Error("Original failure");
} catch (original) {
throw new Error("Higher-level failure", { cause: original });
}
} catch (e) {
console.log(e.message, "caused by:", e.cause.message);
}
Serializing Errors for Logging
An Error object does not serialize usefully with JSON.stringify() by default -- its enumerable-property scan misses name, message, and stack, since they are defined as non-enumerable on the base Error prototype -- so sending or logging error details in JSON form requires manually extracting the properties you need first.
Note: Manually build a plain object with the specific error properties you need (name, message, and any custom ones) before passing it to JSON.stringify(), rather than passing the Error instance directly.
Warning: JSON.stringify(someError) commonly produces an unhelpfully empty object ({}), a frequent surprise for anyone expecting it to capture the error's details automatically.
Example: Serializing Errors for Logging
const err = new Error("Failed");
console.log(JSON.stringify(err)); // "{}" - name/message/stack are non-enumerable
console.log(JSON.stringify({ name: err.name, message: err.message }));
- Assuming every thrown value is actually an Error object -- JavaScript allows throwing any value at all (a string, a number), which lacks the standard name/message/stack properties an Error provides.
- Overwriting or ignoring the stack property when logging an error, losing the exact call-path information that would help pinpoint where the error actually originated.
- Constructing a new Error() without a descriptive message, producing an error that is technically informative to the code but unhelpfully vague to whoever reads the log later.
- new Error(message) creates an Error object with .name ("Error" by default), .message (your description), and .stack (a call trace).
- Built-in error subtypes (TypeError, RangeError, and others) all inherit from the base Error object, sharing its core properties.
- JavaScript technically allows throwing any value, not just an Error object -- but throwing a real Error (or a subclass) is strongly recommended for consistency.
The Error object and its core properties have been supported in every browser since JavaScript's earliest versions.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: