Error Handling in JavaScript: Try, Catch, Finally
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
tryruns normallyIf an error occurs → execution jumps to
catchRemaining
trycode is skippedcatchreceives 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);
}