Skip to main content

Command Palette

Search for a command to run...

Error Handling in JavaScript: Try, Catch, Finally

Updated
5 min read
Error Handling in JavaScript: Try, Catch, Finally

01 — Errors

What Are Errors in JavaScript?

An error is JavaScript's way of saying "I don't know how to continue — something went wrong!" Without handling them, your entire program can crash and stop working.

🧒 Think of it like this: Imagine you're building with LEGO. You reach into the box for a piece… but it's not there! If you don't have a plan for missing pieces, the whole build stops. Errors in JavaScript are the missing pieces. JavaScript has three main kinds of errors:

SyntaxError

Typo in your code. Like spelling a word wrong. JavaScript can't even read it. e.g. missing a bracket {

ReferenceError

Using something that doesn't exist. Like calling a friend who has no phone. e.g. console.log(x) when x was never defined.

TypeError

Using something the wrong way. Like trying to eat soup with a fork. e.g. calling .toUpperCase() on a number.

02 — Try & Catch

Using try and catch Blocks

A try block wraps the risky code. If it explodes, the catch block picks up the pieces — instead of crashing your app, it lets you respond gracefully.

You try to pour juice. If you spill it (error!), your parent catches it with a towel (catch block). The juice didn't ruin the day — someone was ready!

try {
  // 🧪 risky code goes here
  let result = JSON.parse("not valid JSON {{");
  console.log(result);

} catch (error) {
  // 🪤 this runs if something above fails
  console.log("Oops! Something went wrong:", error.message);
}

// Output: "Oops! Something went wrong: Unexpected token n..."

No Error: try block runs fully. catch block is skipped entirely.

💥Error Occurs: try block stops at the error. catch block runs immediately.

The error object inside catch has useful properties: error.message (what went wrong) and error.name (type of error).

03 — Finally

The finally Block

The finally block always runs — whether your code succeeded or failed. It's the clean-up crew that never misses its shift.

After dinner, you always wash your hands — whether you ate all your vegetables or not. finally is the handwashing. It always happens!

function fetchData() {
  console.log("⏳ Loading spinner: ON");

  try {
    // pretend this is an API call
    let data = riskyApiCall(); // might throw!
    console.log("✅ Data received:", data);

  } catch (err) {
    console.log("❌ Failed to load data");

  } finally {
    // This ALWAYS runs — success or failure
    console.log("⏹️  Loading spinner: OFF");
  }
}

Real-world uses for finally: Closing a database connection, hiding a loading spinner, releasing a file lock, or logging that an operation ended. These things must happen regardless of success or failure.

04 — Custom Errors

Throwing Custom Errors

Sometimes you want to create your own errors — when data doesn't look right, or a rule is broken. The throw keyword lets you fire an error on purpose with a message you control.

You're playing a game and someone cheats. You can call it out yourself: "That's not allowed!" — that's like throw. You decide what counts as a problem!

function setAge(age) {
  if (age < 0) {
    // Throw our own error with a clear message
    throw new Error("Age can't be negative, silly! 🙅");
  }
  console.log("Age set to:", age);
}

try {
  setAge(-5);           // ← this will throw
  setAge(25);           // ← this line never runs

} catch (err) {
  console.log("Caught:", err.message);
  // "Caught: Age can't be negative, silly! 🙅"
}

✦ Pro Tips with Custom Errors

  • You can throw a string: throw "Something broke"

  • Better to throw an Error object: throw new Error("msg") — gives you a stack trace

  • You can even create custom error classes by extending the built-in Error class

  • Thrown errors bubble up until something catches them — or the app crashes

05 — Why It Matters

Why Error Handling Matters

Unhandled errors break user experience, hide bugs, and make debugging a nightmare. Good error handling is the mark of a professional developer.

A good driver doesn't crash the car every time it rains. They slow down, turn on wipers, and handle it. Error handling is being a careful driver — ready for anything!

🛡️Prevent Crashes: Stop one bad thing from breaking your entire app.

😊Better UX: Show friendly messages instead of blank screens or cryptic errors.

🔍Easier Debugging: Logged errors tell you exactly what went wrong and where.

🔄Graceful Recovery: Retry operations, use fallback data, or redirect users safely.

// Real-world: fetching user data from an API async function loadUser(userId) { showSpinner(true);

try { const response = await fetch(/api/users/${userId});

if (!response.ok) {
  throw new Error(`HTTP error! Status: ${response.status}`);
}

const user = await response.json();
displayUser(user);   // ✅ show the user

} catch (err) { showErrorMessage("Couldn't load user. Try again!"); console.error("[loadUser] Failed:", err);

} finally { showSpinner(false); // 🔒 always hide the spinner } }

Quick Reference

🗂️ The Complete Cheat Sheet

try: Wrap your risky code here. JavaScript will attempt to run it. If anything throws, execution jumps to catch.

catch: Handles the error. Receives an error object with .message and .name. Only runs when there's an error.

finally: Always runs — error or not. Perfect for cleanup: closing connections, hiding spinners, freeing resources.

throw: Manually trigger an error. Use new Error("message") for best results. You control what counts as a problem.