Docs

4.7-Error-Types-Custom-Errors

4.7 Error Types and Custom Errors

Overview

JavaScript provides several built-in error types and allows you to create custom errors for better error handling and debugging.


Built-in Error Types

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                    Error Hierarchy                          │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                             │
│                         Error                               │
│                           │                                 │
│     ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”   │
│     │         │         │          │         │         │   │
│  SyntaxError ReferenceError TypeError RangeError URIError  │
│                                      │                     │
│                               EvalError                    │
│                               AggregateError               │
│                                                             │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Error Types Summary

Error TypeWhen It Occurs
ErrorGeneric error, base class
SyntaxErrorInvalid JavaScript syntax
ReferenceErrorReference to undefined variable
TypeErrorWrong type or null/undefined access
RangeErrorNumber out of valid range
URIErrorInvalid URI encoding/decoding
EvalErrorError with eval() (legacy)
AggregateErrorMultiple errors wrapped

Error Object Properties

try {
  throw new Error('Something went wrong');
} catch (error) {
  console.log(error.name); // 'Error'
  console.log(error.message); // 'Something went wrong'
  console.log(error.stack); // Stack trace

  // Some browsers also have:
  console.log(error.fileName);
  console.log(error.lineNumber);
  console.log(error.columnNumber);
}

Built-in Error Examples

SyntaxError

// Caught at parse time (before execution)
// eval('const x = ;');  // SyntaxError: Unexpected token ';'

JSON.parse('{invalid}'); // SyntaxError: Unexpected token i

ReferenceError

console.log(undeclaredVariable); // ReferenceError

const obj = {};
obj.method(); // TypeError (not ReferenceError!)

TypeError

null.property; // TypeError: Cannot read properties of null
undefined.method(); // TypeError: Cannot read properties of undefined
const x = 5;
x(); // TypeError: x is not a function
const arr = 1;
arr[0]; // TypeError (in strict mode for assignment)
Object.freeze({}).x = 1; // TypeError in strict mode

RangeError

new Array(-1); // RangeError: Invalid array length
(1234).toString(100); // RangeError: radix must be 2-36
'x'.repeat(-1); // RangeError
function infinite() {
  infinite();
} // RangeError: Maximum call stack exceeded

URIError

decodeURIComponent('%'); // URIError: URI malformed

AggregateError (ES2021)

try {
  throw new AggregateError(
    [new Error('Error 1'), new Error('Error 2'), new TypeError('Type Error')],
    'Multiple errors occurred'
  );
} catch (e) {
  console.log(e.message); // 'Multiple errors occurred'
  console.log(e.errors); // Array of errors
}

// Used with Promise.any()
Promise.any([Promise.reject('fail 1'), Promise.reject('fail 2')]).catch((e) => {
  console.log(e instanceof AggregateError); // true
});

Creating Custom Errors

Basic Custom Error

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

throw new ValidationError('Invalid email format');

Custom Error with Additional Properties

class HttpError extends Error {
  constructor(statusCode, message, body = null) {
    super(message);
    this.name = 'HttpError';
    this.statusCode = statusCode;
    this.body = body;

    // Maintains proper stack trace (V8)
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, HttpError);
    }
  }

  get isClientError() {
    return this.statusCode >= 400 && this.statusCode < 500;
  }

  get isServerError() {
    return this.statusCode >= 500;
  }
}

// Usage
try {
  throw new HttpError(404, 'Resource not found', { id: 123 });
} catch (error) {
  if (error instanceof HttpError) {
    console.log(error.statusCode); // 404
    console.log(error.isClientError); // true
  }
}

Error Hierarchy

// Base application error
class AppError extends Error {
  constructor(message, code) {
    super(message);
    this.name = this.constructor.name;
    this.code = code;
    this.timestamp = new Date();
  }

  toJSON() {
    return {
      name: this.name,
      message: this.message,
      code: this.code,
      timestamp: this.timestamp,
    };
  }
}

// Specific error types
class ValidationError extends AppError {
  constructor(field, message) {
    super(message, 'VALIDATION_ERROR');
    this.field = field;
  }
}

class AuthenticationError extends AppError {
  constructor(message = 'Authentication required') {
    super(message, 'AUTH_ERROR');
  }
}

class NotFoundError extends AppError {
  constructor(resource, id) {
    super(`${resource} with id ${id} not found`, 'NOT_FOUND');
    this.resource = resource;
    this.resourceId = id;
  }
}

// Usage
try {
  throw new NotFoundError('User', 123);
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log(error.resource); // 'User'
    console.log(error.resourceId); // 123
    console.log(error.code); // 'NOT_FOUND'
  }
}

Error Handling Patterns

Checking Error Type

try {
  // Some operation
} catch (error) {
  // By instanceof
  if (error instanceof TypeError) {
    console.log('Type error:', error.message);
  } else if (error instanceof ReferenceError) {
    console.log('Reference error:', error.message);
  } else {
    console.log('Unknown error:', error);
  }

  // By name
  switch (error.name) {
    case 'TypeError':
      // handle
      break;
    case 'ValidationError':
      // handle
      break;
    default:
      throw error; // Re-throw if can't handle
  }
}

Error Wrapping

class DatabaseError extends Error {
  constructor(message, originalError) {
    super(message);
    this.name = 'DatabaseError';
    this.originalError = originalError;
    this.stack = `${this.stack}\nCaused by: ${originalError.stack}`;
  }
}

async function queryDatabase(sql) {
  try {
    return await db.query(sql);
  } catch (error) {
    throw new DatabaseError(`Failed to execute query: ${sql}`, error);
  }
}

Error Cause (ES2022)

try {
  try {
    throw new Error('Database connection failed');
  } catch (dbError) {
    throw new Error('Could not load user', { cause: dbError });
  }
} catch (error) {
  console.log(error.message); // 'Could not load user'
  console.log(error.cause.message); // 'Database connection failed'
}

Best Practices

  1. •Use specific error types - Don't just throw generic Error
  2. •Include context - Add relevant data to error objects
  3. •Preserve stack trace - Use Error.captureStackTrace when extending
  4. •Chain errors - Use cause to preserve original error
  5. •Document error codes - Use consistent error codes
  6. •Don't catch and ignore - Either handle or re-throw

Summary

ConceptDescription
Built-in errorsSyntaxError, TypeError, ReferenceError, etc.
Error propertiesname, message, stack, cause
Custom errorsExtend Error class, add properties
Error hierarchyCreate base AppError, extend for specific types
Error causeChain errors with { cause: originalError }
.7 Error Types Custom Errors - JavaScript Tutorial | DeepML