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 traceYou can even create custom error classes by extending the built-in
ErrorclassThrown 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.



