JavaScript Error Handling & Debugging
Master error handling techniques, try-catch blocks, and debugging strategies for robust JavaScript applications
40 min•By Priygop Team•Last 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