JavaScript provides try/catch/finally for synchronous error handling and .catch() or try/catch with async/await for asynchronous errors, with built-in error types like TypeError, ReferenceError, and SyntaxError.
try wraps risky code, catch receives the error object, finally always runs for cleanup regardless of whether an error occurred
Every error has name (type), message (description), stack (trace), and optionally cause (ES2022) for wrapping errors
TypeError (wrong type usage), ReferenceError (undeclared variable), SyntaxError (invalid syntax), RangeError (value out of range)
Use .catch() for promise chains and try/catch with async/await; forgetting to await inside try/catch lets errors escape
Extend the Error class to create domain-specific errors; enables instanceof checks for targeted handling
Error handling in JavaScript prevents unexpected crashes by letting you catch and respond to runtime problems gracefully. The core mechanism is try...catch...finally, and modern JavaScript extends this to asynchronous code with Promises and async/await.
try block wraps code that might throw. If an exception occurs, control immediately jumps to the catch block — the remaining try code is skipped.catch block receives the error object. You can inspect its name, message, and stack properties to understand what went wrong.finally block always executes regardless of whether an error was thrown. Use it for cleanup (closing connections, releasing resources), not for control flow. Avoid return statements inside finally — they override returns from try or catch.try...catch only catches runtime errors. Syntax errors that prevent the script from parsing cannot be caught.name (the error type, e.g., "TypeError"), message (a human-readable description), and stack (a stack trace showing where the error originated).cause property: throw new Error('Failed', { cause: originalError }). This lets you wrap errors while preserving the original context — essential for debugging deeply nested async operations.TypeError: Using a value in an inappropriate way — calling a non-function, accessing a property of null or undefined. The most common error in JavaScript.ReferenceError: Accessing a variable that hasn't been declared or is in the temporal dead zone (TDZ).SyntaxError: Invalid syntax that prevents parsing. Usually caught at load time, not at runtime — try...catch cannot catch these unless they occur inside eval() or new Function().RangeError: A value is outside its allowed range, such as creating an array with a negative length or exceeding the maximum call stack size..catch() at the end of a chain: fetch(url).then(res => res.json()).catch(err => handle(err)). A .catch() catches errors from any preceding .then() in the chain.await calls in try...catch. An awaited rejected promise throws in the catch block just like a synchronous error.window.addEventListener('unhandledrejection', handler) in browsers or process.on('unhandledRejection', handler) in Node.js.await a promise inside try/catch — the error escapes the catch block entirely because the promise rejects asynchronously.Error: class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; } }. This enables instanceof checks for targeted error handling.error instanceof TypeError vs error instanceof ValidationError to handle different error types differently.Key Interview Distinction: throw vs return
throw transfers control to the nearest catch block and unwinds the call stack. return simply exits the current function. In error handling, always throw actual Error objects (not strings or plain objects) so that you get a proper stack trace and instanceof checks work correctly.
Fun Fact
JavaScript originally had no try/catch at all — it was added in ES3 (1999). Before that, the only error handling was window.onerror, which gave you a string message with no stack trace. The Error object's stack property is still non-standard to this day — every browser formats it differently, which is why libraries like stacktrace.js exist.