Error Handling & Debugging
Key Learning Objectives
Master try/catch/finally control flow and understand how finally return statements override prior returns.
Understand error propagation through the call stack and how unhandled exceptions trigger uncaught error events.
Distinguish built-in error constructors (Error, TypeError, RangeError, SyntaxError, ReferenceError) and custom error subclasses.
Recognize the critical difference between synchronous thrown exceptions and asynchronous rejected promises.
The Interview Problem
What is logged to the console when the following code executes, and why does returning inside a finally block override previous return statements?
1function processData(val) {2 try {3 if (val < 0) {4 throw new RangeError('Must be positive');5 }6 return val * 2;7 } catch (err) {8 if (err instanceof RangeError) {9 return 'handled-range';10 }11 throw err;12 }13}1415function computeWithFinally() {16 try {17 throw new Error('fail');18 } catch (err) {19 return 'from-catch';20 } finally {21 return 'from-finally';22 }23}2425const res1 = processData(5);26const res2 = processData(-1);27const res3 = computeWithFinally();2829console.log(res1, res2, res3);
Predict Console Output
Select the option that matches what standard ECMAScript prints to the console:
10 handled-range from-finally
10 handled-range from-catch
10 unhandled-error from-finally
10 handled-range fail
V8 Engine Execution Trace
Step 1 of 8 (Line 1)Global Execution Context initializes function declarations in the global environment record.
Deep Technical Breakdown
The Mechanics of JavaScript Exception Handling
- Try-Catch-Finally Execution Order:
- The
tryblock executes until normal completion or an exception is thrown. - If an exception is thrown, synchronous execution in
trysuspends immediately and control transfers tocatch (err). - The
finallyblock always runs, whether an error occurred, was caught, or the function attempted to return early.
- The
- The Finally Return Override Trap:
- According to ECMAScript specification §14.15 (The
tryStatement), if afinallyblock exits with an abrupt completion (e.g.return valueorthrow error), that completion overwrites any prior completion fromtryorcatch. - If
catchthrew an error andfinallyreturned a value, the error is swallowed silently and the function returns normally. For this reason, ESLint flagsno-unsafe-finally.
- According to ECMAScript specification §14.15 (The
- Synchronous vs Asynchronous Errors:
try...catchcan only catch synchronous errors in the current call stack turn.- If an asynchronous operation (e.g.
setTimeout, un-awaitedfetch) throws inside a callback, it runs in a different event loop tick; an outertry...catchwill not catch it. - With
async/await,awaitconverts rejected promises into synchronous-style exceptions thattry...catchcan intercept.
Common Traps & Mistakes
Placing a return statement inside a `finally` block, which silently discards uncaught exceptions and creates subtle debugging nightmares.
Wrapping asynchronous callbacks with synchronous `try...catch`: `try { setTimeout(() => { throw new Error(); }, 10); } catch (e) {}` will NOT catch the error.
Catching errors without re-throwing unknown error types: catching everything with an empty `catch {}` masks syntax and reference errors.
FAANG Follow-Up Probes
Probe #1
Why does window.onerror or window.addEventListener('error') capture script errors while window.addEventListener('unhandledrejection') captures unhandled promises?
Probe #2
How does the V8 engine construct an Error stack trace using Error.captureStackTrace(), and how do you customize stack traces in Node.js?
Probe #3
How does React's componentDidCatch and getDerivedStateFromError (Error Boundaries) interact with JavaScript's try/catch model?
