← Back to JavaScript Course | Chapter 8: Error Handling | Lesson 6 of 6

JS Error Object

Every JavaScript error, whether built-in or custom, is ultimately an object with a consistent set of properties -- name (the error type), message (a description), and stack (a trace of where it occurred) -- and understanding this base Error object is what makes all the built-in error types, and custom ones, consistent and predictable to work with.

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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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 }));
Common Mistakes
  1. 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.
  2. 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.
  3. 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.
Chapter Summary
  • 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.
Browser Support

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:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.