Skip to content

08 · Error Handling Basics

🎥 Video walkthrough

try / catch

try {
  const result = JSON.parse("not valid json");
} catch (error) {
  console.log("parsing failed:", error.message);
}

Catching specific error types

JavaScript doesn't have typed catch clauses like some languages — you check error inside the block. Always inspect what you're catching; a bare catch that silently swallows everything hides bugs.

function safeParseInt(value) {
  const parsed = Number(value);
  if (Number.isNaN(parsed)) {
    console.log(`'${value}' is not a valid number`);
    return null;
  }
  return parsed;
}

safeParseInt("42");  // 42
safeParseInt("abc"); // prints message, returns null

finally

try {
  const number = Number("42");
  if (Number.isNaN(number)) throw new Error("conversion failed");
  console.log(`conversion succeeded: ${number}`);
} catch (error) {
  console.log(error.message);
} finally {
  console.log("this always runs"); // cleanup, runs no matter what
}

Raising your own errors

function withdraw(balance, amount) {
  if (amount > balance) {
    throw new Error("insufficient funds");
  }
  return balance - amount;
}

try {
  withdraw(100, 150);
} catch (error) {
  console.log(error.message); // insufficient funds
}

The Error object

try {
  throw new Error("something broke");
} catch (error) {
  console.log(error.name);    // Error
  console.log(error.message); // something broke
  console.log(error.stack);   // stack trace — useful for debugging
}

Errors in asynchronous code (a preview)

try/catch only catches errors thrown synchronously in the same call stack — errors from callbacks or unhandled promise rejections need different handling, covered fully in Level 2 · Module 7.

try {
  setTimeout(() => {
    throw new Error("too late for this try/catch to see me");
  }, 0);
} catch (error) {
  console.log("this never runs");
}

Common built-in error types

Error type When it happens
SyntaxError invalid code, e.g. JSON.parse("{bad")
TypeError wrong type entirely, e.g. calling something that isn't a function
ReferenceError using a variable that doesn't exist
RangeError a value is outside an allowed range, e.g. invalid array length

How It Actually Works

throw doesn't return a value — it unwinds the call stack by walking upward from the current stack frame looking for the nearest enclosing try block whose code region covers the current instruction pointer. V8 tracks this via a per-function table of "protected regions" generated at compile time; when an exception is thrown, the engine consults that table frame by frame, popping stack frames (running any finally blocks it finds along the way) until it either finds a matching catch or reaches the top of the stack, at which point the error becomes an uncaught exception reported to the host (Node prints it and exits; browsers fire a window.onerror event).

The Error object's .stack string is itself lazily computed: when you write new Error(), V8 doesn't format the whole string immediately — it captures a lightweight array of stack frame pointers, and only serializes them into the human-readable "at functionName (file:line:col)" text the first time .stack is actually read. This is a deliberate performance optimization (Error.captureStackTrace exists specifically to control this), because constructing full formatted stack traces for errors that are caught and ignored would be wasted work. finally blocks are guaranteed to run even if the try or catch returns or re-throws, because the engine's unwind logic treats "execute finally" as a mandatory step of leaving the protected region, regardless of how control flow is trying to leave it.

🔀 See this in another language

Exercise

Write a function divideSafely(a, b) that returns the division result, or a descriptive error message string if b is zero or either argument isn't a number — without crashing the program either way.