Skip to main content

Command Palette

Search for a command to run...

Error Handling in JavaScript: Try, Catch, Finally

Published
4 min read

Modern JavaScript applications are expected to be robust, fault-tolerant, and user-friendly. But in reality, things go wrong—APIs fail, user input is invalid, or unexpected runtime conditions occur.

This is where error handling becomes essential.


🚨 A Simple Runtime Error Example

Let’s start with something basic:

let user = null;
console.log(user.name); // ❌ TypeError

This throws:

TypeError: Cannot read properties of null

If not handled, this can crash your app or break user experience.


❓ What Are Errors in JavaScript?

In JavaScript, errors are objects that represent unexpected or invalid operations during execution.

Common Types of Errors:

  • SyntaxError → Invalid code syntax

  • ReferenceError → Using undeclared variables

  • TypeError → Invalid operations on values

  • RangeError → Values out of allowed range

Example:

console.log(x); // ReferenceError: x is not defined

🧠 Why Error Handling Matters

Without proper error handling:

  • Your app may crash abruptly

  • Users get poor experience

  • Debugging becomes difficult

  • Systems become unreliable

With proper handling:

  • You can fail gracefully

  • Show meaningful messages

  • Log errors for debugging

  • Maintain application stability


🛠️ Using try...catch

The try...catch block allows you to catch runtime errors and handle them safely.

Basic Syntax:

try {
  // Code that may throw error
} catch (error) {
  // Handle error
}

Example:

try {
  let user = null;
  console.log(user.name);
} catch (error) {
  console.log("Something went wrong:", error.message);
}

👉 Output:

Something went wrong: Cannot read properties of null

🔍 How It Works Internally

  • Code inside try runs normally

  • If an error occurs → execution jumps to catch

  • Remaining try code is skipped

  • catch receives the error object


🧩 The finally Block

The finally block runs no matter what happens.

Syntax:

try {
  // risky code
} catch (error) {
  // handle error
} finally {
  // always runs
}

Example:

try {
  console.log("Trying...");
  throw new Error("Oops!");
} catch (error) {
  console.log("Caught:", error.message);
} finally {
  console.log("Cleanup done.");
}

👉 Output:

Trying...
Caught: Oops!
Cleanup done.

Use Cases:

  • Closing resources (files, DB connections)

  • Cleanup tasks

  • Logging


🎯 Throwing Custom Errors

JavaScript allows you to create and throw your own errors.

Basic Example:

throw new Error("Something went wrong!");

Custom Validation Example:

function withdraw(amount) {
  if (amount <= 0) {
    throw new Error("Amount must be greater than zero");
  }
  console.log("Withdrawal successful");
}

try {
  withdraw(-100);
} catch (error) {
  console.log(error.message);
}

👉 Output:

Amount must be greater than zero

🧱 Custom Error Types (Advanced)

You can create custom error classes:

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

throw new ValidationError("Invalid input");

⚠️ Important Notes

1. try...catch only works for runtime errors

It does NOT catch:

  • Syntax errors (before execution)

  • Errors outside its block


2. Async Code Needs Special Handling

try {
  fetch("invalid-url"); // ❌ won't be caught
} catch (err) {
  console.log(err);
}

Use async/await:

try {
  const res = await fetch("invalid-url");
} catch (err) {
  console.log("Caught async error:", err);
}

💡 Graceful Failure (Key Concept)

Instead of crashing:

❌ Bad:

console.log(user.name);

✅ Good:

if (user) {
  console.log(user.name);
} else {
  console.log("User not found");
}

👉 Always aim to:

  • Prevent crashes

  • Provide fallback behavior

  • Inform users properly


🐞 Debugging Benefits

Error handling helps you:

  • Capture error messages (error.message)

  • Track stack traces (error.stack)

  • Log issues for production debugging

Example:

catch (error) {
  console.error(error.stack);
}