Lesson 15 of 33
Handling Errors and Debugging
Learn how to use try, catch, finally and throw to handle errors gracefully, and how to use browser DevTools to find where a bug actually happened.
When you start writing code, errors can feel scary — like failing a test. But in programming, errors are just messages from the browser telling you that something did not go as expected.
Every professional developer writes code that throws errors every day. The difference between a beginner and an experienced developer is not that the experienced one writes perfect code; it is that they know how to catch errors, handle them gracefully, and find the cause quickly using debugging tools.
Think of error handling like wearing a safety harness when climbing a playground wall. If you fall, the harness catches you so you do not hurt yourself, and you can keep climbing.
Types of error in JavaScript
Before we learn how to catch errors, let us review the three main kinds of errors you will run into:
SyntaxError
Grammar rules broken (missing closing bracket or quote). The browser rejects the whole file before running a single line.
ReferenceError
Using a variable name that was never created or declared.
TypeError
Operation invalid for that data type (e.g. calling (42).toUpperCase() on a number).
Understanding which type of error you have points you directly toward the fix.
Note the difference in when these happen. A SyntaxError is found before the file runs, which means try...catch — the tool we are about to learn — cannot catch it: the try block never gets the chance to execute. try...catch only helps with errors that happen while your code is already running, like the ReferenceError and TypeError above.
Catching errors with try...catch
When JavaScript hits an unhandled error during runtime, it stops executing the rest of the script. This can freeze a user's screen or break a button on a web page.
To prevent your program from crashing, you can wrap risky code in a try...catch block:
- Step 1
Code runs in try block
Executes line by line normally
- Step 2
Error is thrown
Bypasses remaining try block lines
- Step 3
Enters catch block
Captures error object safely
- Step 4
Enters finally block
Guaranteed cleanup, success or failure
try {
// Risky code goes here
const result = 10 / unknownVariable;
console.log("This line will not run if an error happens above");
} catch (error) {
// This code runs only if an error happened inside the try block
console.log("Caught an error:", error.message);
}If everything in try works smoothly, catch is completely ignored. But if any line inside try throws an error, JavaScript jumps straight into the catch block, passing an Error object into the variable (usually named error or err).
The error object has two very helpful properties:
error.name: The category of error (likeTypeErrororReferenceError).error.message: A short sentence explaining what went wrong.
Cleaning up with finally
Sometimes, whether your code succeeded or failed, you need to clean up afterward — such as turning off a "Loading..." animation or closing an open file connection.
That is where finally comes in:
try {
console.log("Attempting a task...");
// Imagine fetching data here
} catch (error) {
console.log("Task failed:", error.message);
} finally {
console.log("This always runs at the end, success or failure");
}No matter what happens — even if try succeeds or catch handles an error — the finally block is guaranteed to run.
Throwing your own errors with throw
You do not have to wait for JavaScript to run into a problem automatically. You can create and throw your own errors when something invalid happens in your program using the throw keyword:
function checkAge(age) {
if (age < 0) {
throw new Error("Age cannot be negative!");
}
if (age < 13) {
return "Kid account created";
}
return "Standard account created";
}
try {
console.log(checkAge(-5));
} catch (err) {
console.log("Validation error:", err.message);
}By throwing explicit errors when bad input arrives, you catch the problem at the edge of your program rather than deep inside it, where the cause is much harder to trace.
Try it yourself
Run this as it is, and read the order of the output. Then add a call with two valid numbers and confirm the finally line still prints.
Debugging with DevTools
When your code does not do what you expected, do not guess. Reach for your browser's Developer Tools.
Top — runs next
console.log(): The simplest tool. Print out values at step A, step B, and step C to see where the value changed unexpectedly.- Breakpoints in F12 DevTools: Open inspect element (F12), go to the Sources or Debugger tab, click on any line number in your JavaScript file. When your page runs, JavaScript will pause execution right at that line.
- Inspecting Variables: While paused at a breakpoint, hover over any variable to see its current value, or step through your code line-by-line using the "Step Over" (
F10) and "Step Into" (F11) buttons. - The Call Stack: While paused, look at the "Call Stack" panel on the right. It lists the exact chain of functions that called each other to reach this spot, like breadcrumbs trailing through your code.
What to remember
Errors are normal signals, not failures. Wrap unpredictable code in try...catch, put cleanup that must always happen in finally, throw your own errors when input is invalid, and use breakpoints and the call stack to inspect your code while it is still running.
Check yourself
4 questions · pass 3/4 to unlock Changing the Page
1.What is the main purpose of a try...catch block in JavaScript?
2.What does the throw keyword do?
3.When does code inside a finally block execute?
4.What is a call stack in JavaScript debugging?
4 left to answer