Skip to main content

Command Palette

Search for a command to run...

Try, Catch, Finally in javascript

Write safer, more reliable code with proper error handling

Updated
6 min read
Try, Catch, Finally in javascript
A

Hi, I’m Abdul Samad. A web development learner and tech enthusiast. I write about what I learn, share practical coding tips, and publish in-depth blogs on programming and modern web development.

Check out my full collection of blogs on Hashnode: https://abdulsamad30.hashnode.dev/

Connect with me on X for quick updates and insights: @abdul_sama60108

Every developer has been there. You write what feels like perfectly clean code, run it, and then boom the browser console lights up red. Something broke. Maybe a variable was undefined. Maybe an API returned something you didn't expect. Maybe a user typed letters into a field that was supposed to accept numbers.

This is just the reality of writing JavaScript. Things go wrong. The question isn't whether errors will happen it's whether your code is ready for them when they do.


What Are Errors in JavaScript?

Before you can handle errors, you need to understand what they actually are.

In JavaScript, an error is an object that gets created when something goes wrong at runtime. Notice the word runtime. We're not talking about syntax errors that get caught before your code even runs we're talking about errors that only show up once the code is actually executing.

Think of it like driving a car. A syntax error is like having a flat tire before you even leave the driveway the car won't move. A runtime error is like the engine suddenly failing while you're already on the highway. The car started fine, but something went wrong mid-journey.

Here's what that looks like in code:

const user = null;
console.log(user.name);

Output:

TypeError: Cannot read properties of null (reading 'name')

The code looks innocent enough until you try to access .name on something that doesn't exist. JavaScript throws a TypeError and your program stops.

Other common runtime errors include:

  • ReferenceError trying to use a variable that was never declared

  • RangeError passing a value outside the allowed range (like a negative array length)

  • TypeError using a value in a way that doesn't match its type

These errors, if left unhandled, will crash your program right there. That's what error handling is designed to prevent.


Using Try and Catch Blocks

The most fundamental tool for error handling in JavaScript is the try...catch block. The idea is straightforward: you try to run some code, and if it throws an error, you catch it instead of letting it crash everything.

A good analogy here is a net under a trapeze artist. The performer (your code) still does the act, but if they fall (an error occurs), he net (the catch block) is there to stop them from hitting the ground.

function getUserAge(user) {
  try {
    console.log("User's age is: " + user.age);
  } catch (error) {
    console.log("Something went wrong: " + error.message);
  }
}

getUserAge(null);

Output:

Something went wrong: Cannot read properties of null (reading 'age')

Instead of crashing the program, the error is caught and handled gracefully. The user sees a readable message. The program keeps running.

The error object passed into catch has a few useful properties worth knowing:

  • error.message the human-readable description of what went wrong

  • error.name the type of error (e.g., TypeError, ReferenceError)

  • error.stack a full trace of where the error originated, useful for debugging

This is what graceful failure means. The program doesn't pretend nothing happened, but it also doesn't fall apart completely. It handles the situation and moves on.


The Finally Block

try and catch handle the error, but there's a third piece: finally. Code inside a finally block runs no matter what whether the try block succeeded, whether an error was caught, whether the world is on fire. It always executes.

Think of it like locking the door when you leave your house. You leave whether the weather is good or bad, whether the trip went well or terribly. The door still gets locked either way.

This makes finally perfect for cleanup tasks closing a database connection, hiding a loading spinner, releasing a resource.

function fetchData() {
  try {
    console.log("Fetching data...");
    throw new Error("Network failure");
  } catch (error) {
    console.log("Error caught: " + error.message);
  } finally {
    console.log("Cleaning up — this always runs.");
  }
}

fetchData();

Output:

Fetching data...
Error caught: Network failure
Cleaning up — this always runs.

Even though an error was thrown and caught, the finally block ran. This is the execution order JavaScript always follows


Throwing Custom Errors

JavaScript lets you throw your own errors using the throw keyword. This means you're not limited to waiting for the runtime to throw something you can decide yourself when something has gone wrong and raise an error manually.

The real-world equivalent is a bouncer at a club. The club has its own rules: under 18, no entry. The bouncer doesn't wait for something to break he proactively throws you out if you don't meet the requirements.

function divide(a, b) {
  if (b === 0) {
    throw new Error("Division by zero is not allowed.");
  }
  return a / b;
}

try {
  const result = divide(10, 0);
  console.log("Result: " + result);
} catch (error) {
  console.log("Caught an error: " + error.message);
}

Output:

Caught an error: Division by zero is not allowed.

You can throw any value a string, a number, an object but throwing an actual Error object is the right move because it preserves the stack trace and integrates properly with catch.

For more structured error handling, you can also extend the built-in Error class to create custom error types:

class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = "ValidationError";
  }
}

function validateUsername(username) {
  if (username.length < 3) {
    throw new ValidationError("Username must be at least 3 characters.");
  }
  return true;
}

try {
  validateUsername("ab");
} catch (error) {
  console.log(error.name + ": " + error.message);
}

Output:

ValidationError: Username must be at least 3 characters.

Now your catch block knows exactly what kind of error it's dealing with. That's powerful, especially in larger codebases.


Why Error Handling Matters

At this point you might be thinking: "my code mostly works, do I really need all this?" Yes, and here's why.

First, it's about the user experience. An unhandled error doesn't just print something in the console it can freeze a UI, break a form, or leave the user staring at a blank screen with no idea what happened. Handled errors mean you can show a friendly message instead: "Something went wrong, please try again."

Second, it dramatically improves your ability to debug. When you catch an error and log error.stack, you get a full trace of where things went wrong. Without error handling, some errors fail silently in ways that are nearly impossible to trace back.

Third, real applications talk to APIs, databases, and user input three sources of chaos that are completely outside your control. A user pastes weird characters into a form. An API goes down. A JSON response comes back malformed. Without error handling, any of these can break your entire app. With it, you handle each scenario on your own terms.

Error handling isn't about being pessimistic about your code.

It's about being realistic about the environment your code runs in. Things will go wrong. Build for that, and your code becomes something people can actually rely on.

FIN ✌️