Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /Javascript
  4. /Throwing Custom Errors
Your Progress0%
0 of 61 completed

JavaScript Topics

Getting Started

  • What is JavaScript?
  • JavaScript vs Other Languages
  • Setting Up Your JavaScript Environment
  • Developer Console and Debugging Basics

JavaScript Fundamentals

  • Variables (var, let, const)
  • Data Types and Type Checking
  • Comments and Code Organization
  • String Methods and Template Literals
  • Type Conversion and Coercion
  • Operators (Arithmetic, Comparison, Logical)

Control Flow

  • If/Else Statements
  • Switch Statements
  • Ternary Operator
  • Truthy and Falsy Values
  • Logical Operators and Short-Circuiting

Loops and Iteration

  • For Loops
  • While and Do-While Loops
  • For...of and For...in Loops
  • Break and Continue Statements

Functions

  • Function Declarations and Expressions
  • Arrow Functions
  • Function Parameters and Arguments
  • Return Statements and Values
  • Scope and Closures
  • Higher-Order Functions and Callbacks

Arrays

  • Array Basics and Creation
  • Array Methods: Adding and Removing Elements
  • Array Iteration Methods (forEach, map, filter)
  • Array Methods: find, some, every, reduce
  • Sorting, Reversing, and Array Transformation

Objects

  • Object Basics and Properties
  • Object Methods and 'this' Keyword
  • Object Destructuring
  • Object.keys, values, entries
  • Spread Operator and Object Cloning

DOM Manipulation

  • Introduction to the DOM
  • Selecting Elements (querySelector, getElementById)
  • Modifying Elements (textContent, innerHTML, style)
  • Creating and Removing Elements
  • Event Listeners and Event Handling
  • Event Object and Event Delegation

Asynchronous JavaScript

  • Understanding Asynchronous Code
  • setTimeout and setInterval
  • Callbacks and Callback Hell
  • Promises and Promise Chaining
  • Async/Await

Error Handling

  • Try, Catch, Finally
  • Throwing Custom Errors

Modern JavaScript Features

  • ES6+ Overview
  • Modules (import/export)
  • Optional Chaining and Nullish Coalescing
  • Rest and Spread Operators

Working with Data

  • JSON (Parse and Stringify)
  • Fetch API and HTTP Requests
  • Working with Local Storage

Advanced Concepts

  • Classes and Object-Oriented Programming
  • Array and Object Advanced Patterns
  • Regular Expressions Basics

Best Practices & Real-World Application

  • JavaScript Best Practices
  • Debugging Techniques and Tools
  • Building a Complete Interactive Project

Throwing Custom Errors

Creating meaningful error types for better code

While catching errors is essential, sometimes you need to throw your own errors to signal that something went wrong. JavaScript's throw statement lets you create and throw errors intentionally, allowing you to validate inputs, enforce business rules, and communicate problems clearly. By creating custom error classes, you can build sophisticated error handling systems where different error types are handled differently. This lesson teaches you to throw errors like a pro and build robust, self-documenting error systems!

The throw Statement

Basic throw Syntax

JAVASCRIPT
// Throw stops execution and creates an error
function divide(a, b) {
  if (b === 0) {
    throw new Error('Cannot divide by zero');
    // Execution stops here!
    console.log('This never runs');
  }
  return a / b;
}

try {
  console.log(divide(10, 2));  // 5
  console.log(divide(10, 0));  // Throws error!
  console.log('This never runs');
} catch (error) {
  console.log('Caught:', error.message);
}

// Output:
// 5
// Caught: Cannot divide by zero

// throw immediately stops execution and jumps to catch

You Can throw Any Value

JAVASCRIPT
// Throwing strings (not recommended)
try {
  throw 'Something went wrong';
} catch (error) {
  console.log(error);  // "Something went wrong"
}

// Throwing numbers
try {
  throw 404;
} catch (error) {
  console.log('Error code:', error);  // 404
}

// Throwing objects
try {
  throw { code: 404, message: 'Not found' };
} catch (error) {
  console.log(error.code, error.message);
}

// Best practice: Throw Error objects
try {
  throw new Error('This is best practice');
} catch (error) {
  console.log(error.message);
  console.log(error.stack);  // Has stack trace!
}

// Error objects provide more information

Best Practice: Always throw Error objects (or custom error classes that extend Error) because they automatically include stack traces for debugging.

The Error Constructor

JAVASCRIPT
// Creating Error objects
let error1 = new Error('Something went wrong');
console.log(error1.name);     // "Error"
console.log(error1.message);  // "Something went wrong"
console.log(error1.stack);    // Stack trace

// You can throw it
throw error1;

// Or throw inline
throw new Error('Inline error');

// Error with detailed message
function validateAge(age) {
  if (age < 0) {
    throw new Error(`Invalid age: ${age}. Age cannot be negative.`);
  }
  if (age < 18) {
    throw new Error(`Age ${age} is below minimum required age of 18.`);
  }
  return true;
}

try {
  validateAge(-5);
} catch (error) {
  console.log(error.message);
  // Invalid age: -5. Age cannot be negative.
}

Built-in Error Types

JAVASCRIPT
// Different built-in error types

// TypeError - Wrong type
function processString(str) {
  if (typeof str !== 'string') {
    throw new TypeError('Expected a string');
  }
  return str.toUpperCase();
}

// RangeError - Value out of range
function setAge(age) {
  if (age < 0 || age > 150) {
    throw new RangeError('Age must be between 0 and 150');
  }
}

// ReferenceError - Invalid reference
// Usually thrown automatically, but can throw manually
function getVariable(name) {
  if (!window[name]) {
    throw new ReferenceError(`Variable '${name}' is not defined`);
  }
  return window[name];
}

// Using specific error types helps with debugging
try {
  processString(123);
} catch (error) {
  if (error instanceof TypeError) {
    console.log('Type error:', error.message);
  }
}

Custom Error Classes

Creating Custom Errors

JAVASCRIPT
// Basic custom error class
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
  }
}

// Using custom error
function validateEmail(email) {
  if (!email) {
    throw new ValidationError('Email is required');
  }
  if (!email.includes('@')) {
    throw new ValidationError('Invalid email format');
  }
  return true;
}

// Catching custom errors
try {
  validateEmail('invalid');
} catch (error) {
  if (error instanceof ValidationError) {
    console.log('Validation failed:', error.message);
  } else {
    console.log('Unknown error:', error.message);
  }
}

// Output: Validation failed: Invalid email format

Custom Error with Additional Properties

JAVASCRIPT
// Enhanced custom error
class ValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.name = 'ValidationError';
    this.field = field;  // Additional property
    this.timestamp = new Date();
  }
}

// Usage
function validateUser(user) {
  if (!user.email) {
    throw new ValidationError('Email is required', 'email');
  }
  if (!user.password) {
    throw new ValidationError('Password is required', 'password');
  }
  if (user.password.length < 8) {
    throw new ValidationError('Password too short', 'password');
  }
}

try {
  validateUser({ email: 'user@test.com', password: 'short' });
} catch (error) {
  console.log('Error:', error.message);
  console.log('Field:', error.field);
  console.log('Time:', error.timestamp);
}

// Output:
// Error: Password too short
// Field: password
// Time: 2024-01-01T12:00:00.000Z

Multiple Custom Error Types

JAVASCRIPT
// Define multiple error types
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
  }
}

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

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

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

// Using different error types
function processRequest(data) {
  // Validation
  if (!data.userId) {
    throw new ValidationError('User ID is required');
  }
  
  // Authentication
  if (!data.token) {
    throw new AuthenticationError('No authentication token');
  }
  
  // Network simulation
  if (!navigator.onLine) {
    throw new NetworkError('No internet connection', 0);
  }
  
  // Database simulation
  if (data.userId < 0) {
    throw new DatabaseError('Invalid user ID', `SELECT * FROM users WHERE id = ${data.userId}`);
  }
}

// Handle different errors differently
try {
  processRequest({ userId: -1, token: 'abc' });
} catch (error) {
  if (error instanceof ValidationError) {
    showFormError(error.message);
  } else if (error instanceof AuthenticationError) {
    redirectToLogin();
  } else if (error instanceof NetworkError) {
    showOfflineMessage();
  } else if (error instanceof DatabaseError) {
    logDatabaseError(error.query);
    showServerError();
  } else {
    showGenericError();
  }
}

Input Validation Pattern

JAVASCRIPT
// Comprehensive validation with custom errors
class ValidationError extends Error {
  constructor(message, field, value) {
    super(message);
    this.name = 'ValidationError';
    this.field = field;
    this.value = value;
  }
}

function validateUserRegistration(data) {
  let errors = [];
  
  // Username validation
  if (!data.username) {
    errors.push(new ValidationError('Username is required', 'username', data.username));
  } else if (data.username.length < 3) {
    errors.push(new ValidationError('Username too short (min 3)', 'username', data.username));
  } else if (data.username.length > 20) {
    errors.push(new ValidationError('Username too long (max 20)', 'username', data.username));
  }
  
  // Email validation
  if (!data.email) {
    errors.push(new ValidationError('Email is required', 'email', data.email));
  } else if (!data.email.includes('@')) {
    errors.push(new ValidationError('Invalid email format', 'email', data.email));
  }
  
  // Password validation
  if (!data.password) {
    errors.push(new ValidationError('Password is required', 'password', null));
  } else if (data.password.length < 8) {
    errors.push(new ValidationError('Password too short (min 8)', 'password', null));
  }
  
  // Age validation
  if (data.age !== undefined) {
    if (typeof data.age !== 'number') {
      errors.push(new ValidationError('Age must be a number', 'age', data.age));
    } else if (data.age < 0) {
      errors.push(new ValidationError('Age cannot be negative', 'age', data.age));
    } else if (data.age < 18) {
      errors.push(new ValidationError('Must be 18 or older', 'age', data.age));
    }
  }
  
  // If any errors, throw them
  if (errors.length > 0) {
    throw errors;
  }
  
  return true;
}

// Usage
try {
  validateUserRegistration({
    username: 'ab',
    email: 'invalid',
    password: 'short',
    age: 15
  });
  console.log('Validation passed');
} catch (errors) {
  console.log('Validation failed:');
  errors.forEach(error => {
    console.log(`  - ${error.field}: ${error.message}`);
  });
}

// Output:
// Validation failed:
//   - username: Username too short (min 3)
//   - email: Invalid email format
//   - password: Password too short (min 8)
//   - age: Must be 18 or older

Error Hierarchies

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

// Specific error types extending base
class ClientError extends AppError {
  constructor(message, statusCode = 400) {
    super(message);
    this.statusCode = statusCode;
  }
}

class ServerError extends AppError {
  constructor(message, statusCode = 500) {
    super(message);
    this.statusCode = statusCode;
  }
}

// More specific errors
class ValidationError extends ClientError {
  constructor(message, field) {
    super(message, 400);
    this.field = field;
  }
}

class AuthenticationError extends ClientError {
  constructor(message) {
    super(message, 401);
  }
}

class AuthorizationError extends ClientError {
  constructor(message) {
    super(message, 403);
  }
}

class NotFoundError extends ClientError {
  constructor(resource) {
    super(`${resource} not found`, 404);
    this.resource = resource;
  }
}

class DatabaseError extends ServerError {
  constructor(message, query) {
    super(message, 500);
    this.query = query;
  }
}

// Usage with hierarchy
function handleRequest(request) {
  try {
    if (!request.user) {
      throw new AuthenticationError('Not authenticated');
    }
    
    if (!request.user.isAdmin) {
      throw new AuthorizationError('Admin access required');
    }
    
    let resource = findResource(request.resourceId);
    if (!resource) {
      throw new NotFoundError('Resource');
    }
    
    return processResource(resource);
  } catch (error) {
    if (error instanceof ClientError) {
      sendClientError(error.statusCode, error.message);
    } else if (error instanceof ServerError) {
      logServerError(error);
      sendServerError(error.statusCode);
    } else {
      sendGenericError();
    }
  }
}

Practical Patterns

Pattern 1: Guard Clauses

JAVASCRIPT
// Validate early, throw early
function processPayment(amount, cardNumber, cvv) {
  // Guard clause 1: Amount
  if (amount <= 0) {
    throw new Error('Amount must be positive');
  }
  
  // Guard clause 2: Card number
  if (!cardNumber || cardNumber.length !== 16) {
    throw new Error('Invalid card number');
  }
  
  // Guard clause 3: CVV
  if (!cvv || cvv.length !== 3) {
    throw new Error('Invalid CVV');
  }
  
  // All validations passed, process payment
  return chargeCard(amount, cardNumber, cvv);
}

// Clear, readable, fails fast

Pattern 2: Assertion Functions

JAVASCRIPT
// Assertion helper
function assert(condition, message) {
  if (!condition) {
    throw new Error(`Assertion failed: ${message}`);
  }
}

function calculateDiscount(price, percentage) {
  assert(price > 0, 'Price must be positive');
  assert(percentage >= 0 && percentage <= 100, 'Percentage must be 0-100');
  
  return price * (percentage / 100);
}

// Type assertion
function assertType(value, type, name) {
  if (typeof value !== type) {
    throw new TypeError(`${name} must be a ${type}`);
  }
}

function greet(name, age) {
  assertType(name, 'string', 'Name');
  assertType(age, 'number', 'Age');
  
  console.log(`Hello ${name}, you are ${age} years old`);
}

Pattern 3: Error Factory

JAVASCRIPT
// Error factory for consistent error creation
const ErrorFactory = {
  validation: (field, message) => {
    let error = new Error(message);
    error.name = 'ValidationError';
    error.field = field;
    return error;
  },
  
  notFound: (resource) => {
    let error = new Error(`${resource} not found`);
    error.name = 'NotFoundError';
    error.resource = resource;
    error.statusCode = 404;
    return error;
  },
  
  unauthorized: (message = 'Unauthorized') => {
    let error = new Error(message);
    error.name = 'UnauthorizedError';
    error.statusCode = 401;
    return error;
  },
  
  server: (message) => {
    let error = new Error(message);
    error.name = 'ServerError';
    error.statusCode = 500;
    return error;
  }
};

// Usage
function getUser(userId) {
  if (!userId) {
    throw ErrorFactory.validation('userId', 'User ID is required');
  }
  
  let user = database.findUser(userId);
  if (!user) {
    throw ErrorFactory.notFound('User');
  }
  
  return user;
}

Practical Examples

Example 1: API Error Handler

JAVASCRIPT
// Complete API error system
class APIError extends Error {
  constructor(message, statusCode, details) {
    super(message);
    this.name = 'APIError';
    this.statusCode = statusCode;
    this.details = details;
    this.timestamp = new Date().toISOString();
  }
  
  toJSON() {
    return {
      error: this.name,
      message: this.message,
      statusCode: this.statusCode,
      details: this.details,
      timestamp: this.timestamp
    };
  }
}

async function fetchUserData(userId) {
  try {
    // Validation
    if (!userId) {
      throw new APIError('User ID is required', 400, { field: 'userId' });
    }
    
    // Make request
    let response = await fetch(`/api/users/${userId}`);
    
    // Handle HTTP errors
    if (response.status === 404) {
      throw new APIError('User not found', 404, { userId });
    }
    
    if (response.status === 401) {
      throw new APIError('Authentication required', 401);
    }
    
    if (response.status >= 500) {
      throw new APIError('Server error', response.status, {
        endpoint: `/api/users/${userId}`
      });
    }
    
    if (!response.ok) {
      throw new APIError('Request failed', response.status);
    }
    
    // Parse response
    let data = await response.json();
    return data;
    
  } catch (error) {
    if (error instanceof APIError) {
      console.error('API Error:', error.toJSON());
      throw error;
    } else {
      // Network error or parsing error
      let apiError = new APIError('Network error', 0, { originalError: error.message });
      console.error('Network Error:', apiError.toJSON());
      throw apiError;
    }
  }
}

// Usage
try {
  let user = await fetchUserData(123);
  console.log('User loaded:', user);
} catch (error) {
  if (error.statusCode === 404) {
    showMessage('User not found');
  } else if (error.statusCode === 401) {
    redirectToLogin();
  } else if (error.statusCode >= 500) {
    showMessage('Server error. Please try again later.');
  } else if (error.statusCode === 0) {
    showMessage('Network error. Check your connection.');
  }
}

Example 2: Form Validation System

JAVASCRIPT
// Comprehensive form validation
class FormValidationError extends Error {
  constructor(errors) {
    super('Form validation failed');
    this.name = 'FormValidationError';
    this.errors = errors;  // Array of field errors
  }
  
  hasError(field) {
    return this.errors.some(e => e.field === field);
  }
  
  getError(field) {
    return this.errors.find(e => e.field === field);
  }
  
  getAllErrors() {
    return this.errors.map(e => `${e.field}: ${e.message}`);
  }
}

class Validator {
  constructor() {
    this.errors = [];
  }
  
  required(value, field) {
    if (!value) {
      this.errors.push({ field, message: 'This field is required' });
    }
    return this;
  }
  
  email(value, field) {
    if (value && !value.includes('@')) {
      this.errors.push({ field, message: 'Invalid email format' });
    }
    return this;
  }
  
  minLength(value, length, field) {
    if (value && value.length < length) {
      this.errors.push({ field, message: `Minimum ${length} characters required` });
    }
    return this;
  }
  
  maxLength(value, length, field) {
    if (value && value.length > length) {
      this.errors.push({ field, message: `Maximum ${length} characters allowed` });
    }
    return this;
  }
  
  pattern(value, regex, field, message) {
    if (value && !regex.test(value)) {
      this.errors.push({ field, message });
    }
    return this;
  }
  
  validate() {
    if (this.errors.length > 0) {
      throw new FormValidationError(this.errors);
    }
  }
}

// Usage
function validateRegistrationForm(formData) {
  let validator = new Validator();
  
  validator
    .required(formData.username, 'username')
    .minLength(formData.username, 3, 'username')
    .maxLength(formData.username, 20, 'username')
    
    .required(formData.email, 'email')
    .email(formData.email, 'email')
    
    .required(formData.password, 'password')
    .minLength(formData.password, 8, 'password')
    .pattern(
      formData.password,
      /[A-Z]/,
      'password',
      'Must contain at least one uppercase letter'
    )
    .pattern(
      formData.password,
      /[0-9]/,
      'password',
      'Must contain at least one number'
    )
    
    .validate();
}

// Using the validator
try {
  validateRegistrationForm({
    username: 'ab',
    email: 'invalid',
    password: 'short'
  });
  
  console.log('Form valid - submit!');
  submitForm();
  
} catch (error) {
  if (error instanceof FormValidationError) {
    console.log('Validation errors:');
    error.getAllErrors().forEach(err => console.log('  -', err));
    
    // Display errors in UI
    displayFormErrors(error.errors);
  }
}

// Output:
// Validation errors:
//   - username: Minimum 3 characters required
//   - email: Invalid email format
//   - password: Minimum 8 characters required
//   - password: Must contain at least one uppercase letter
//   - password: Must contain at least one number

Custom Error Mastery

Create and throw custom errors

Output:
Click "Run" to execute your code...
💡 Tip: Use console.log() to see your output in the console above.

Key Takeaways

  • throw stops execution and creates an error
  • Can throw any value, but Error objects are best practice
  • Error objects include stack traces for debugging
  • Create custom error classes by extending Error
  • Custom errors can have additional properties (field, statusCode, etc.)
  • Use instanceof to check error types
  • Different error types enable different handling strategies
  • Guard clauses validate early and throw early
  • Error hierarchies organize related error types
  • Always provide clear, actionable error messages

What's Next?

Congratulations! You've completed the Error Handling section and now know how to throw custom errors, create error classes, and build sophisticated error handling systems. You can validate inputs, enforce business rules, and handle different error types appropriately.

You've now completed 49 out of 62 JavaScript tutorial pages (79% complete)! You've mastered 11 major sections. You're getting very close to completing the entire JavaScript tutorial series!

💪 Practice Challenge:

Before moving on, try creating:

  1. A custom ValidationError class with field and value properties
  2. A function that throws errors for invalid inputs
  3. Multiple custom error types (Network, Auth, Database)
  4. A form validator that throws arrays of validation errors
  5. An error handling system that handles different types differently

Test Your Understanding

Question 1 / 4

What does the 'throw' keyword do?

Score: 0 / 0

Mastering custom errors in JavaScript! 🎯

Previous
Try, Catch, Finally
Next
ES6+ Overview

Continue Learning JavaScript

Join 2,000+ developers mastering JavaScript. New lessons weekly - 100% FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

JavaScript Tutorials

0 of 61 completed

Your Progress0%

Topics

Getting Started

  • What is JavaScript?
  • JavaScript vs Other Languages
  • Setting Up Your JavaScript Environment
  • Developer Console and Debugging Basics

JavaScript Fundamentals

  • Variables (var, let, const)
  • Data Types and Type Checking
  • Comments and Code Organization
  • String Methods and Template Literals
  • Type Conversion and Coercion
  • Operators (Arithmetic, Comparison, Logical)

Control Flow

  • If/Else Statements
  • Switch Statements
  • Ternary Operator
  • Truthy and Falsy Values
  • Logical Operators and Short-Circuiting

Loops and Iteration

  • For Loops
  • While and Do-While Loops
  • For...of and For...in Loops
  • Break and Continue Statements

Functions

  • Function Declarations and Expressions
  • Arrow Functions
  • Function Parameters and Arguments
  • Return Statements and Values
  • Scope and Closures
  • Higher-Order Functions and Callbacks

Arrays

  • Array Basics and Creation
  • Array Methods: Adding and Removing Elements
  • Array Iteration Methods (forEach, map, filter)
  • Array Methods: find, some, every, reduce
  • Sorting, Reversing, and Array Transformation

Objects

  • Object Basics and Properties
  • Object Methods and 'this' Keyword
  • Object Destructuring
  • Object.keys, values, entries
  • Spread Operator and Object Cloning

DOM Manipulation

  • Introduction to the DOM
  • Selecting Elements (querySelector, getElementById)
  • Modifying Elements (textContent, innerHTML, style)
  • Creating and Removing Elements
  • Event Listeners and Event Handling
  • Event Object and Event Delegation

Asynchronous JavaScript

  • Understanding Asynchronous Code
  • setTimeout and setInterval
  • Callbacks and Callback Hell
  • Promises and Promise Chaining
  • Async/Await

Error Handling

  • Try, Catch, Finally
  • Throwing Custom Errors

Modern JavaScript Features

  • ES6+ Overview
  • Modules (import/export)
  • Optional Chaining and Nullish Coalescing
  • Rest and Spread Operators

Working with Data

  • JSON (Parse and Stringify)
  • Fetch API and HTTP Requests
  • Working with Local Storage

Advanced Concepts

  • Classes and Object-Oriented Programming
  • Array and Object Advanced Patterns
  • Regular Expressions Basics

Best Practices & Real-World Application

  • JavaScript Best Practices
  • Debugging Techniques and Tools
  • Building a Complete Interactive Project
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo