JavaScript Error Handling

Catch What Goes Wrong

JavaScript Error Handling

`try`/`catch`/`finally` lets you recover from errors instead of crashing. The same syntax works for sync and async errors.

4 min read Level 2/5 #errors#try-catch#finally
What you'll learn
  • Catch a thrown error
  • Use `finally` for cleanup
  • Decide what to catch and what to rethrow

When something throws, JavaScript unwinds the stack until it finds a try/catch block. If nothing catches it, the program crashes.

The Basic Shape

try / catch script.js
try {
  const data = JSON.parse("not valid json");
  console.log(data);
} catch (e) {
  console.log("parse failed:", e.message);
}

console.log("program continues");
▶ Preview: console

catch receives the thrown value. It’s almost always an Error object with .message, .name, and .stack properties.

finally — Always Runs

finally runs whether the try succeeded, threw, or returned early. Use it for cleanup.

finally script.js
function fetchSomething() {
  console.log("start");
  try {
    throw new Error("oops");
  } catch (e) {
    console.log("caught:", e.message);
    return "recovered";
  } finally {
    console.log("cleanup");      // runs even with the early return
  }
}

console.log(fetchSomething());
// "start"
// "caught: oops"
// "cleanup"
// "recovered"
▶ Preview: console

With async/await

try/catch works for async errors too — a rejected Promise that’s awaited becomes a thrown error.

try/catch in async script.js
async function load() {
  try {
    const response = await fetch("/nope");
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return await response.json();
  } catch (e) {
    console.log("load failed:", e.message);
    return null;
  }
}
▶ Preview: console

This is one of the biggest wins of async/await over Promise chains — error handling is the same as for sync code.

What To Catch

Be specific. Don’t wrap your whole program in a giant try — catching errors you didn’t expect can hide bugs.

// Bad — too broad. Hides bugs.
try {
  // 200 lines of logic
} catch (e) {
  console.log("something went wrong");
}

// Better — narrow scope, specific recovery.
try {
  const parsed = JSON.parse(rawInput);
  return parsed;
} catch (e) {
  return defaultValue;
}

Rethrowing

Sometimes you want to catch, do something (log, clean up), and re-throw so the error continues up.

async function readConfig(path) {
  try {
    return await readFile(path);
  } catch (e) {
    console.error(`Failed to read ${path}:`, e.message);
    throw e;   // let the caller see the failure too
  }
}

Catching Specific Error Types

If you throw different error subclasses, you can branch on type:

Branch by error type script.js
function maybeFail() {
  throw new TypeError("bad input");
}

try {
  maybeFail();
} catch (e) {
  if (e instanceof TypeError) {
    console.log("type error:", e.message);
  } else if (e instanceof RangeError) {
    console.log("range error:", e.message);
  } else {
    throw e;   // unknown error — let it propagate
  }
}
▶ Preview: console

We’ll cover the built-in error types in the next lesson.

Optional catch Binding

You can leave out the (e) if you don’t need it.

try {
  doSomething();
} catch {
  // we don't care what the error was
  showDefault();
}

Up Next

Throwing errors yourself.

JavaScript throw →