Docs

4.6-Try-Catch

4.6 Error Handling with try-catch

Overview

Error handling is a critical aspect of robust JavaScript programming. The try...catch statement allows you to handle runtime errors gracefully, preventing your application from crashing and providing meaningful feedback to users.


Table of Contents

  1. •What Are Errors?
  2. •The try...catch Statement
  3. •The finally Block
  4. •Error Object
  5. •Throwing Errors
  6. •Error Types
  7. •Custom Errors
  8. •Nested try-catch
  9. •Error Handling Patterns
  10. •Best Practices

What Are Errors?

Errors in JavaScript can occur for many reasons:

CategoryExamples
Syntax ErrorsTypos, missing brackets, invalid code
Reference ErrorsUsing undefined variables
Type ErrorsCalling non-functions, accessing null properties
Range ErrorsInvalid array length, recursion limit
Runtime ErrorsNetwork failures, file not found
Custom ErrorsBusiness logic violations
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                    Error Categories                      │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                          │
│  Compile-Time Errors          Runtime Errors             │
│  ──────────────────          ──────────────              │
│  • Syntax errors              • Type errors              │
│  • Cannot be caught           • Reference errors         │
│  • Code won't run             • Range errors             │
│                               • Custom errors            │
│                               • CAN be caught            │
│                                                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

The try...catch Statement

Basic Syntax

try {
  // Code that might throw an error
  riskyOperation();
} catch (error) {
  // Handle the error
  console.log('An error occurred:', error.message);
}

How It Works

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                    try...catch Flow                    │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                        │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”                                           │
│  │   try   │                                           │
│  │  block  │                                           │
│  ā””ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”˜                                           │
│       │                                                │
│       ā”œā”€ā”€ā”€ā”€ No Error ────► Continue after try-catch    │
│       │                                                │
│       └──── Error ────► ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”                   │
│                         │  catch  │                    │
│                         │  block  │                    │
│                         ā””ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”˜                    │
│                              │                         │
│                              ā–¼                         │
│                     Continue execution                 │
│                                                        │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Example

try {
  const result = JSON.parse('invalid json');
  console.log(result);
} catch (error) {
  console.log('Failed to parse JSON:', error.message);
}

console.log('Program continues...');

The finally Block

The finally block executes regardless of whether an error occurred.

Syntax

try {
  // Attempt operation
} catch (error) {
  // Handle error
} finally {
  // Always executes
  // Cleanup code goes here
}

Use Cases

// Resource cleanup
let file;
try {
  file = openFile('data.txt');
  processFile(file);
} catch (error) {
  console.error('Processing failed:', error);
} finally {
  if (file) {
    file.close(); // Always close the file
  }
}

finally with Return Statements

function test() {
  try {
    return 'try';
  } finally {
    console.log('finally runs'); // This executes!
  }
}

console.log(test());
// "finally runs"
// "try"

try...finally (without catch)

function riskyOperation() {
  try {
    // Do something that might fail
    return performTask();
  } finally {
    // Cleanup regardless of success/failure
    cleanup();
  }
}

Error Object

When an error is caught, the catch block receives an Error object.

Error Properties

PropertyDescription
nameType of error (e.g., "TypeError")
messageHuman-readable error description
stackStack trace (non-standard but widely supported)

Accessing Error Information

try {
  undefined.property;
} catch (error) {
  console.log('Name:', error.name); // "TypeError"
  console.log('Message:', error.message); // "Cannot read property..."
  console.log('Stack:', error.stack); // Full stack trace
}

Optional Catch Binding (ES2019+)

// If you don't need the error object
try {
  riskyOperation();
} catch {
  console.log('Something went wrong');
}

Throwing Errors

throw Statement

You can throw your own errors using the throw statement.

throw new Error('Something went wrong');

What Can Be Thrown?

// Preferred: Error objects
throw new Error('Error message');
throw new TypeError('Type error message');
throw new RangeError('Range error message');

// Possible but not recommended
throw 'Error string'; // Just a string
throw 42; // A number
throw { code: 500 }; // An object
throw true; // A boolean

Custom Validation

function divide(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new TypeError('Both arguments must be numbers');
  }
  if (b === 0) {
    throw new Error('Cannot divide by zero');
  }
  return a / b;
}

try {
  console.log(divide(10, 2)); // 5
  console.log(divide(10, 0)); // Throws Error
} catch (error) {
  console.error(error.message);
}

Error Types

JavaScript has several built-in error types:

TypeCause
ErrorGeneric error (base class)
SyntaxErrorInvalid JavaScript syntax
ReferenceErrorReference to undefined variable
TypeErrorValue is not expected type
RangeErrorNumber outside allowable range
URIErrorInvalid URI encoding/decoding
EvalErrorError in eval() (rarely used)
AggregateErrorMultiple errors wrapped together

Examples

// ReferenceError
undefinedVariable; // ReferenceError

// TypeError
null.property; // TypeError
const x = 5;
x(); // TypeError: x is not a function

// RangeError
new Array(-1); // RangeError: Invalid array length
(1).toFixed(200); // RangeError

// SyntaxError (usually at parse time, can't be caught)
// JSON.parse can throw catchable SyntaxError
JSON.parse('{invalid}'); // SyntaxError

// URIError
decodeURIComponent('%'); // URIError

Checking Error Type

try {
  riskyOperation();
} catch (error) {
  if (error instanceof TypeError) {
    console.log('Type error:', error.message);
  } else if (error instanceof ReferenceError) {
    console.log('Reference error:', error.message);
  } else {
    console.log('Other error:', error.message);
  }
}

Custom Errors

Creating Custom Error Classes

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

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

// Usage
function validateEmail(email) {
  if (!email.includes('@')) {
    throw new ValidationError('Invalid email format');
  }
}

try {
  validateEmail('invalid-email');
} catch (error) {
  if (error instanceof ValidationError) {
    console.log('Validation failed:', error.message);
  } else {
    throw error; // Re-throw unexpected errors
  }
}

Error Hierarchy

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

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

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

Nested try-catch

Basic Nesting

try {
  try {
    throw new Error('Inner error');
  } catch (innerError) {
    console.log('Inner catch:', innerError.message);
    throw new Error('Outer error'); // Re-throw
  }
} catch (outerError) {
  console.log('Outer catch:', outerError.message);
}

Re-throwing Errors

function processData(data) {
  try {
    return JSON.parse(data);
  } catch (error) {
    // Log for debugging
    console.error('Parse error:', error);
    // Re-throw with more context
    throw new Error(`Failed to process data: ${error.message}`);
  }
}

Error Handling Patterns

1. Guard Clause Pattern

function processUser(user) {
  if (!user) {
    throw new Error('User is required');
  }
  if (!user.email) {
    throw new Error('User email is required');
  }

  // Main logic after validation
  return sendEmail(user.email);
}

2. Fail-Fast Pattern

function initialize(config) {
  if (!config.apiKey) {
    throw new Error('API key is required');
  }
  if (!config.endpoint) {
    throw new Error('Endpoint is required');
  }

  // Continue initialization...
}

3. Error Wrapper Pattern

function wrapError(fn, errorMessage) {
  return function (...args) {
    try {
      return fn(...args);
    } catch (error) {
      throw new Error(`${errorMessage}: ${error.message}`);
    }
  };
}

const safeParseJSON = wrapError(JSON.parse, 'JSON parsing failed');

4. Default Value Pattern

function safeParse(json, defaultValue = null) {
  try {
    return JSON.parse(json);
  } catch {
    return defaultValue;
  }
}

console.log(safeParse('{"valid": true}')); // { valid: true }
console.log(safeParse('invalid')); // null

5. Error Aggregation

function validateForm(data) {
  const errors = [];

  if (!data.name) {
    errors.push('Name is required');
  }
  if (!data.email) {
    errors.push('Email is required');
  }
  if (data.age < 0) {
    errors.push('Age must be positive');
  }

  if (errors.length > 0) {
    throw new AggregateError(errors, 'Validation failed');
  }

  return true;
}

6. Async Error Handling (Preview)

async function fetchData(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Fetch failed:', error.message);
    throw error;
  }
}

Best Practices

1. Be Specific About What You Catch

// āŒ Too broad
try {
  // Many operations
  const data = fetchData();
  const parsed = parseData(data);
  const result = processData(parsed);
} catch (error) {
  console.log('Something failed');
}

// āœ… Specific error handling
try {
  const data = fetchData();
  try {
    const parsed = parseData(data);
  } catch (parseError) {
    throw new Error(`Parse failed: ${parseError.message}`);
  }
  const result = processData(parsed);
} catch (error) {
  console.log('Specific error:', error.message);
}

2. Don't Swallow Errors

// āŒ Bad: Error is silently ignored
try {
  riskyOperation();
} catch (error) {
  // Doing nothing
}

// āœ… Good: Error is logged or handled
try {
  riskyOperation();
} catch (error) {
  console.error('Operation failed:', error);
  // Or: throw error; (re-throw)
  // Or: return defaultValue;
}

3. Use Appropriate Error Types

// āŒ Generic error
throw new Error('Invalid input');

// āœ… Specific error type
throw new TypeError('Expected a string');
throw new RangeError('Value must be between 1 and 100');
throw new ValidationError('Email format is invalid');

4. Include Contextual Information

// āŒ Not helpful
throw new Error('Failed');

// āœ… Helpful error message
throw new Error(`Failed to save user ${userId}: ${originalError.message}`);

5. Always Clean Up Resources

let connection;
try {
  connection = openDatabase();
  // Do operations
} catch (error) {
  console.error('Database error:', error);
} finally {
  if (connection) {
    connection.close(); // Always cleanup
  }
}

6. Validate Early

function createUser(name, age) {
  // Validate at the start
  if (typeof name !== 'string') {
    throw new TypeError('Name must be a string');
  }
  if (typeof age !== 'number' || age < 0) {
    throw new TypeError('Age must be a positive number');
  }

  // Main logic after validation
  return { name, age, createdAt: new Date() };
}

Summary

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                 Error Handling Quick Reference           │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                          │
│  try {                                                   │
│      // Code that might throw                            │
│  } catch (error) {                                       │
│      // Handle error                                     │
│      // error.name, error.message, error.stack          │
│  } finally {                                             │
│      // Always runs - cleanup code                       │
│  }                                                       │
│                                                          │
│  Throwing: throw new Error("message");                   │
│                                                          │
│  Built-in Types:                                         │
│  • Error, TypeError, ReferenceError                      │
│  • RangeError, SyntaxError, URIError                    │
│                                                          │
│  Best Practices:                                         │
│  • Be specific about errors                              │
│  • Don't swallow errors silently                        │
│  • Include helpful messages                              │
│  • Always clean up in finally                            │
│  • Validate inputs early                                 │
│                                                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Next Steps

  • •Practice with the examples in examples.js
  • •Complete the exercises in exercises.js
  • •Learn about async error handling with Promises
  • •Explore logging and monitoring strategies
.6 Try Catch - JavaScript Tutorial | DeepML