Skip to main content
Course/Module 2/Topic 4 of 4Intermediate

JavaScript Error Handling & Debugging

Master error handling techniques, try-catch blocks, and debugging strategies for robust JavaScript applications

40 minBy Priygop TeamLast updated: Feb 2026

Error Handling Fundamentals

Error handling is crucial for building robust applications. JavaScript provides try-catch blocks and error objects to handle exceptions gracefully and prevent application crashes.

Try-Catch Blocks

Example
// Basic try-catch
try {
    const result = 10 / 0;
    console.log(result);
} catch (error) {
    console.error('An error occurred:', error.message);
}

// Try-catch with finally
try {
    const data = JSON.parse('invalid json');
    console.log(data);
} catch (error) {
    console.error('JSON parsing error:', error.message);
} finally {
    console.log('This always executes');
}

// Multiple catch blocks (not supported in JavaScript)
// Use if-else instead
try {
    const result = someRiskyOperation();
    console.log(result);
} catch (error) {
    if (error instanceof TypeError) {
        console.error('Type error:', error.message);
    } else if (error instanceof ReferenceError) {
        console.error('Reference error:', error.message);
    } else {
        console.error('Unknown error:', error.message);
    }
}

// Async error handling
async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Fetch error:', error.message);
        return null;
    }
}

// Custom error classes
class ValidationError extends Error {
    constructor(message, field) {
        super(message);
        this.name = 'ValidationError';
        this.field = field;
    }
}

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

// Using custom errors
function validateUser(user) {
    if (!user.name) {
        throw new ValidationError('Name is required', 'name');
    }
    if (!user.email) {
        throw new ValidationError('Email is required', 'email');
    }
    if (user.age < 0) {
        throw new ValidationError('Age must be positive', 'age');
    }
}

try {
    validateUser({ name: '', email: 'test@example.com', age: 25 });
} catch (error) {
    if (error instanceof ValidationError) {
        console.error(`Validation error in ${error.field}: ${error.message}`);
    } else {
        console.error('Unknown error:', error.message);
    }
}

Error Handling Patterns

Example
// Error handling patterns
// 1. Error boundary pattern
function withErrorBoundary(operation, fallback) {
    try {
        return operation();
    } catch (error) {
        console.error('Operation failed:', error);
        return fallback;
    }
}

const result = withErrorBoundary(
    () => JSON.parse('invalid json'),
    { error: 'Failed to parse data' }
);

// 2. Retry pattern
async function retryOperation(operation, maxRetries = 3) {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
        try {
            return await operation();
        } catch (error) {
            if (attempt === maxRetries) {
                throw error;
            }
            console.log(`Attempt ${attempt} failed, retrying...`);
            await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
        }
    }
}

// 3. Error logging
class ErrorLogger {
    constructor() {
        this.errors = [];
    }
    
    log(error, context = {}) {
        const errorEntry = {
            timestamp: new Date(),
            error: error.message,
            stack: error.stack,
            context
        };
        
        this.errors.push(errorEntry);
        console.error('Error logged:', errorEntry);
        
        // In production, send to error tracking service
        this.sendToErrorService(errorEntry);
    }
    
    sendToErrorService(errorEntry) {
        // Simulate sending to error tracking service
        console.log('Sending to error service:', errorEntry);
    }
    
    getErrors() {
        return this.errors;
    }
}

const logger = new ErrorLogger();

// 4. Graceful degradation
function loadFeature(featureName) {
    try {
        const feature = require(`./features/${featureName}`);
        return feature;
    } catch (error) {
        console.warn(`Feature ${featureName} not available, using fallback`);
        return getFallbackFeature(featureName);
    }
}

function getFallbackFeature(featureName) {
    const fallbacks = {
        'advanced-chart': () => ({ render: () => console.log('Basic chart rendered') }),
        'real-time-updates': () => ({ start: () => console.log('Polling updates started') })
    };
    
    return fallbacks[featureName] || (() => ({ render: () => console.log('Default feature') }));
}

// 5. Input validation
function validateInput(input, schema) {
    const errors = [];
    
    for (const [field, rules] of Object.entries(schema)) {
        const value = input[field];
        
        if (rules.required && !value) {
            errors.push(`${field} is required`);
        }
        
        if (value && rules.type && typeof value !== rules.type) {
            errors.push(`${field} must be a ${rules.type}`);
        }
        
        if (value && rules.min && value < rules.min) {
            errors.push(`${field} must be at least ${rules.min}`);
        }
        
        if (value && rules.max && value > rules.max) {
            errors.push(`${field} must be at most ${rules.max}`);
        }
    }
    
    if (errors.length > 0) {
        throw new ValidationError(errors.join(', '));
    }
    
    return true;
}

// Usage
const userSchema = {
    name: { required: true, type: 'string' },
    age: { required: true, type: 'number', min: 0, max: 120 },
    email: { required: true, type: 'string' }
};

try {
    validateInput({ name: 'John', age: 25, email: 'john@example.com' }, userSchema);
    console.log('Input is valid');
} catch (error) {
    console.error('Validation failed:', error.message);
}

Practice Exercise: Error Handling

Example
// Exercise: Build a Robust Calculator
class SafeCalculator {
    constructor() {
        this.history = [];
    }
    
    add(a, b) {
        try {
            if (typeof a !== 'number' || typeof b !== 'number') {
                throw new TypeError('Both arguments must be numbers');
            }
            
            const result = a + b;
            this.history.push({ operation: 'add', a, b, result });
            return result;
        } catch (error) {
            this.logError(error, { operation: 'add', a, b });
            throw error;
        }
    }
    
    divide(a, b) {
        try {
            if (typeof a !== 'number' || typeof b !== 'number') {
                throw new TypeError('Both arguments must be numbers');
            }
            
            if (b === 0) {
                throw new Error('Division by zero');
            }
            
            const result = a / b;
            this.history.push({ operation: 'divide', a, b, result });
            return result;
        } catch (error) {
            this.logError(error, { operation: 'divide', a, b });
            throw error;
        }
    }
    
    sqrt(number) {
        try {
            if (typeof number !== 'number') {
                throw new TypeError('Argument must be a number');
            }
            
            if (number < 0) {
                throw new Error('Cannot calculate square root of negative number');
            }
            
            const result = Math.sqrt(number);
            this.history.push({ operation: 'sqrt', number, result });
            return result;
        } catch (error) {
            this.logError(error, { operation: 'sqrt', number });
            throw error;
        }
    }
    
    logError(error, context) {
        console.error('Calculator error:', {
            message: error.message,
            type: error.constructor.name,
            context,
            timestamp: new Date()
        });
    }
    
    getHistory() {
        return this.history;
    }
    
    clearHistory() {
        this.history = [];
    }
}

// Exercise: Build an API Client with Error Handling
class ApiClient {
    constructor(baseUrl) {
        this.baseUrl = baseUrl;
        this.retryAttempts = 3;
        this.retryDelay = 1000;
    }
    
    async request(endpoint, options = {}) {
        let lastError;
        
        for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
            try {
                const response = await fetch(`${this.baseUrl}${endpoint}`, {
                    headers: {
                        'Content-Type': 'application/json',
                        ...options.headers
                    },
                    ...options
                });
                
                if (!response.ok) {
                    throw new NetworkError(
                        `HTTP ${response.status}: ${response.statusText}`,
                        response.status
                    );
                }
                
                return await response.json();
            } catch (error) {
                lastError = error;
                
                if (attempt < this.retryAttempts) {
                    console.log(`Attempt ${attempt} failed, retrying in ${this.retryDelay}ms...`);
                    await this.delay(this.retryDelay * attempt);
                }
            }
        }
        
        throw lastError;
    }
    
    delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    async getUsers() {
        return this.request('/users');
    }
    
    async createUser(userData) {
        return this.request('/users', {
            method: 'POST',
            body: JSON.stringify(userData)
        });
    }
    
    async updateUser(id, userData) {
        return this.request(`/users/${id}`, {
            method: 'PUT',
            body: JSON.stringify(userData)
        });
    }
    
    async deleteUser(id) {
        return this.request(`/users/${id}`, {
            method: 'DELETE'
        });
    }
}

// Test the error handling
const calculator = new SafeCalculator();

try {
    console.log(calculator.add(5, 3)); // 8
    console.log(calculator.divide(10, 2)); // 5
    console.log(calculator.sqrt(16)); // 4
    console.log(calculator.divide(10, 0)); // Error: Division by zero
} catch (error) {
    console.error('Calculator error:', error.message);
}

console.log('Calculator history:', calculator.getHistory());

Try It Yourself — Control Flow & Logic

Try It Yourself — Control Flow & LogicHTML
HTML Editor
✓ ValidTab = 2 spaces
HTML|40 lines|1816 chars|✓ Valid syntax
UTF-8

Quick Quiz — Control Flow & Logic

Chat on WhatsApp
Priygop - Leading Professional Development Platform | Expert Courses & Interview Prep