JS Error Handling & Debugging
Catch issues quickly and make your apps reliable.
try-catchthrowdebuggerTable of Contents
JavaScript Error Handling & Debugging: A Complete Tutorial
Errors are inevitable in programming. Great developers stand out by how they prevent, catch, investigate, and fix errors. This guide covers practical techniques to build reliable JavaScript applications.
1. Types of Errors in JavaScript
Syntax Errors
// console.log("Hello World"; // missing parenthesis
// const = 10; // invalid assignment
// function myFunction() { return true; // missing closing braceReference Errors
// console.log(x); // x is not defined
// console.log(myVar); let myVar = 5; // TDZ
const userName = "Alice";
// console.log(useName); // typo -> ReferenceErrorType Errors
// const user = null; console.log(user.name);
const PI = 3.14;
// PI = 3.14159; // Assignment to constant variable
// "Hello".push("World"); // not a functionRange, URI, Eval
// new Array(-1); // RangeError
// decodeURIComponent("%"); // URIError
try { throw new EvalError("Eval error message"); } catch (error) { console.log(error.name); }Error Hierarchy
const errors = [new Error("Base"), new SyntaxError("Syntax"), new ReferenceError("Ref"), new TypeError("Type"), new RangeError("Range"), new URIError("URI")];
errors.forEach(err => console.log(err instanceof Error, err.name, err.message));2. Try...Catch...Finally (The Basics)
Basic try/catch
try {
const result = riskyOperation();
console.log("Success:", result);
} catch (error) {
console.error("Something went wrong:", error.message);
}try/catch/finally
function processData(data) {
let connection = null;
try {
connection = openDatabaseConnection();
return connection.query("SELECT * FROM users");
} catch (error) {
console.error("Database error:", error);
return null;
} finally {
if (connection) connection.close();
}
}Safe JSON Parse
function safeJSONParse(jsonString, fallback = null) {
try { return JSON.parse(jsonString); }
catch (error) { console.warn("Failed to parse JSON:", error.message); return fallback; }
}Conditional Catch
function robustOperation(value) {
try {
if (typeof value !== "number") throw new TypeError("Value must be a number");
if (value < 0) throw new RangeError("Value must be non-negative");
return Math.sqrt(value);
} catch (error) {
if (error instanceof TypeError) return 0;
if (error instanceof RangeError) return 0;
return null;
}
}3. Error Objects and Custom Errors
try { throw new Error("Something went wrong"); }
catch (error) { console.log(error.name, error.message, error.stack); }Custom Error Classes
class AppError extends Error {
constructor(message, statusCode = 500) {
super(message); this.name = this.constructor.name; this.statusCode = statusCode;
}
}
class ValidationError extends AppError {
constructor(message, field) { super(message, 400); this.field = field; }
}
class DatabaseError extends AppError {
constructor(message, query) { super(message, 500); this.query = query; }
}Error Cause (ES2022)
async function fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`, { cause: { status: response.status, userId } });
return await response.json();
} catch (error) {
throw new Error(`Failed to fetch user ${userId}`, { cause: error });
}
}4. Debugging Techniques
Strategic Logging
function processOrder(order) {
console.log("[processOrder] start", { id: order.id, items: order.items.length });
const validated = validateOrder(order);
console.log("[processOrder] validated", validated);
return saveOrder(validated);
}Groups and Assertions
console.group("Complex Calculation");
console.assert(typeof price === "number", "price must be a number");
console.groupEnd();Conditional Logging
const ENABLE_DEBUG = true;
function debugLog(...args) { if (ENABLE_DEBUG) console.log("[DEBUG]", ...args); }5. Console API Mastery
console.log("Standard log");
console.info("Info");
console.warn("Warning");
console.error("Error");
console.table([{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]);
console.time("API Call");
console.timeEnd("API Call");
console.count("Processing user");
console.trace("Called here");Custom Styled Logs
console.log("%cWarning", "color: orange; font-weight: bold");
const customConsole = {
success: (...a) => console.log("%c✓ " + a[0], "color: green; font-weight: bold", ...a.slice(1)),
error: (...a) => console.log("%c✗ " + a[0], "color: red; font-weight: bold", ...a.slice(1))
};6. Breakpoints and Debugger Statement
function calculateTotal(items, taxRate) {
let subtotal = 0;
for (const item of items) {
debugger;
subtotal += item.price * item.quantity;
}
debugger;
return subtotal + subtotal * taxRate;
}Conditional Breakpoint Pattern
function processOrder(order) {
if (order.total > 1000) debugger;
if (order.userId === "problematic-user") debugger;
}7. Common Errors and Solutions
TDZ / ReferenceError
let myVar = 5;
console.log(myVar);Cannot read property of undefined/null
const user = getUser();
console.log(user?.name ?? "Anonymous");X is not a function
if (typeof obj.getName === "function") obj.getName();Maximum call stack size exceeded
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}Network Robust Fetch
async function robustFetch(url, options = {}) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), options.timeout || 10000);
try {
const response = await fetch(url, { ...options, signal: controller.signal });
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
return await response.json();
} finally {
clearTimeout(timeoutId);
}
}8. Error Logging and Reporting
Simple Error Logger
class ErrorLogger {
constructor() { this.errors = []; this.maxStored = 100; }
log(error, context = {}) {
const entry = { timestamp: new Date().toISOString(), name: error.name, message: error.message, stack: error.stack, context };
this.errors.unshift(entry);
if (this.errors.length > this.maxStored) this.errors.pop();
}
}Global Handlers
window.addEventListener("error", event => {
console.error("Unhandled error:", event.error);
});
window.addEventListener("unhandledrejection", event => {
console.error("Unhandled promise rejection:", event.reason);
});9. Best Practices and Patterns
Defensive Programming
function processUser(user) {
if (!user || typeof user !== "object") throw new TypeError("User must be an object");
if (!user.id || typeof user.id !== "number") throw new Error("User id must be numeric");
return { ...user, displayName: user.name?.toUpperCase?.() ?? "UNKNOWN" };
}Safe Object Access
const safeGet = (obj, path, defaultValue) => {
try { return path.split(".").reduce((acc, key) => acc?.[key], obj) ?? defaultValue; }
catch { return defaultValue; }
};Async Error Wrapper
function asyncErrorHandler(asyncFn) {
return (...args) => asyncFn(...args).catch(error => {
console.error(`Error in ${asyncFn.name}:`, error);
return null;
});
}Retry with Backoff
async function retryWithBackoff(asyncFunction, maxRetries = 3, initialDelay = 1000) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try { return await asyncFunction(); }
catch (error) {
lastError = error;
if (attempt === maxRetries) break;
await new Promise(r => setTimeout(r, initialDelay * Math.pow(2, attempt - 1)));
}
}
throw new Error(`Failed after ${maxRetries} retries: ${lastError.message}`);
}Summary: Error Handling & Debugging Quick Reference
Error Types
SyntaxError: code cannot be parsed.ReferenceError: variable does not exist.TypeError: invalid operation for given type.RangeError: value out of allowed range.URIError: URI handling issue.
Debugging Tools
| Tool | Best For |
|---|---|
console.log() | Quick checks and values |
console.table() | Object/array inspection |
console.time() | Performance timing |
debugger | Paused execution context |
| Breakpoints | Complex flow debugging |
console.trace() | Call stack trace |
Best Practices Checklist
- Validate function inputs.
- Use optional chaining and nullish coalescing.
- Handle Promise rejections.
- Log with context, not generic messages.
- Use custom error types for clear flows.
- Avoid leaking sensitive data in error logs.
- Gracefully degrade when features fail.
- Use retry logic and request timeouts.
- Report production errors to monitoring services.
Related: Asynchronous JS, Performance.