JavaScriptError HandlingIntermediate

Custom Error classes

Defines new error types by extending the built-in `Error` class, allowing for more specific error handling and identification.

Review the syntaxStudy the examplesOpen the coding app
class CustomError extends Error { constructor(message, options) { super(message, options); this.name = 'CustomError'; // Best practice } }

This static page keeps the syntax and examples indexed for search, while the coding app handles interactive exploration and saved references.

What it does

Overview

Defines new error types by extending the built-in `Error` class, allowing for more specific error handling and identification.

Custom error classes provide a powerful way to create domain-specific error types, making error handling in applications more robust, semantic, and maintainable. By extending the built-in `Error` class (e.g., `class ValidationError extends Error`), your custom errors automatically inherit essential properties like `message` and `stack`, which are crucial for debugging. The primary benefit of custom errors is that you can use `instanceof` to identify specific error types in your `catch` blocks, allowing for differentiated error handling logic. For example, you might handle a `NetworkError` by retrying the request, a `ValidationError` by displaying specific form feedback, and a generic `Error` by logging it and showing an 'unexpected error' message. When creating a custom error class, it's a best practice to call `super(message, options)` in the constructor to correctly initialize the `Error` base class and to explicitly set `this.name` to the class name (e.g., `this.name = 'ValidationError'`) for better stack trace readability and consistency across environments. Custom errors significantly improve code clarity, enable more precise error recovery strategies, and enhance the overall developer experience in complex applications.

Quick reference

Syntax

class CustomError extends Error { constructor(message, options) { super(message, options); this.name = 'CustomError'; // Best practice } }

See it in practice

Examples

1

Basic custom error class

class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
  }
}

function validateInput(value) {
  if (!value || value.length < 5) {
    throw new ValidationError('Input must be at least 5 characters long.');
  }
  return true;
}

try {
  validateInput('abc');
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(`Validation Failed: ${error.message}`);
  } else {
    console.error(`An unexpected error occurred: ${error.message}`);
  }
}
Output:
Validation Failed: Input must be at least 5 characters long.

A `ValidationError` class extends `Error`. The `catch` block uses `instanceof` to specifically identify and handle validation errors, demonstrating type-based error handling.

2

Custom error with additional properties

class NetworkError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.name = 'NetworkError';
    this.statusCode = statusCode;
  }
}

async function fetchData(url) {
  const response = await fetch(url);
  if (!response.ok) {
    throw new NetworkError(`Failed to fetch ${url}`, response.status);
  }
  return response.json();
}

async function runFetch() {
  try {
    await fetchData('https://nonexistent.api/data');
  } catch (error) {
    if (error instanceof NetworkError) {
      console.error(`Network Error (${error.statusCode}): ${error.message}`);
      // Specific handling for network issues, e.g., retry logic
    } else {
      console.error(`Generic Error: ${error.message}`);
    }
  }
}

runFetch();
Output:
Network Error (404): Failed to fetch https://nonexistent.api/data

The `NetworkError` includes a `statusCode` property, providing more context. The `catch` block can then access this property for more granular error handling or logging.

3

Chaining custom errors with `Error.cause`

class DatabaseError extends Error {
  constructor(message, options) {
    super(message, options);
    this.name = 'DatabaseError';
  }
}

class DataProcessingError extends Error {
  constructor(message, options) {
    super(message, options);
    this.name = 'DataProcessingError';
  }
}

function saveToDb(data) {
  try {
    // Simulate a database operation that fails
    throw new Error('Connection refused by DB server.');
  } catch (dbErr) {
    throw new DatabaseError('Could not save data to database.', { cause: dbErr });
  }
}

function processAndSave(payload) {
  try {
    // Simulate data processing logic
    if (!payload.isValid) {
      throw new Error('Payload is invalid.');
    }
    saveToDb(payload);
  } catch (procErr) {
    throw new DataProcessingError('Failed to process and save payload.', { cause: procErr });
  }
}

try {
  processAndSave({ isValid: false });
} catch (finalError) {
  console.error(`Top-level Error: ${finalError.name}: ${finalError.message}`);
  let current = finalError;
  while (current.cause) {
    current = current.cause;
    console.error(`  Caused by: ${current.name || 'Error'}: ${current.message}`);
  }
}
Output:
Top-level Error: DataProcessingError: Failed to process and save payload. Caused by: Error: Payload is invalid.

This demonstrates chaining custom errors. A `DataProcessingError` wraps a generic `Error`, and if `saveToDb` were to fail, a `DatabaseError` would wrap that. The top-level catch can then traverse the `cause` chain.

Debug faster

Common Errors

1

Forgetting to call `super()` in the constructor

Cause: If `super()` is not called in the constructor of a custom error class, `this` will not be initialized, leading to a `ReferenceError` or incorrect behavior of the inherited `Error` properties (like `stack`).

Fix: Always call `super(message, options)` as the first statement in your custom error class constructor to properly initialize the base `Error` class.

class MyError extends Error {
  constructor(message) {
    // super(message); // Missing call to super()
    this.name = 'MyError';
  }
}

try {
  throw new MyError('Test');
} catch (e) {
  // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
}

Runtime support

Compatibility

Node.js, DenoAll modernES6 (Classes), ES2022 (Error.cause in super)

Source: MDN Web Docs

Common questions

Frequently Asked Questions

Defines new error types by extending the built-in `Error` class, allowing for more specific error handling and identification.

Forgetting to call `super()` in the constructor: Always call `super(message, options)` as the first statement in your custom error class constructor to properly initialize the base `Error` class.