Catch What Goes Wrong
JavaScript Error Handling
`try`/`catch`/`finally` lets you recover from errors instead of crashing. The same syntax works for sync and async errors.
What you'll learn
- Catch a thrown error
- Use `finally` for cleanup
- Decide what to catch and what to rethrow
When something throws, JavaScript unwinds the stack until it finds a
try/catch block. If nothing catches it, the program crashes.
The Basic Shape
try {
const data = JSON.parse("not valid json");
console.log(data);
} catch (e) {
console.log("parse failed:", e.message);
}
console.log("program continues"); catch receives the thrown value. It’s almost always an Error
object with .message, .name, and .stack properties.
finally — Always Runs
finally runs whether the try succeeded, threw, or returned early.
Use it for cleanup.
function fetchSomething() {
console.log("start");
try {
throw new Error("oops");
} catch (e) {
console.log("caught:", e.message);
return "recovered";
} finally {
console.log("cleanup"); // runs even with the early return
}
}
console.log(fetchSomething());
// "start"
// "caught: oops"
// "cleanup"
// "recovered" With async/await
try/catch works for async errors too — a rejected Promise that’s
awaited becomes a thrown error.
async function load() {
try {
const response = await fetch("/nope");
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (e) {
console.log("load failed:", e.message);
return null;
}
} This is one of the biggest wins of async/await over Promise
chains — error handling is the same as for sync code.
What To Catch
Be specific. Don’t wrap your whole program in a giant try —
catching errors you didn’t expect can hide bugs.
// Bad — too broad. Hides bugs.
try {
// 200 lines of logic
} catch (e) {
console.log("something went wrong");
}
// Better — narrow scope, specific recovery.
try {
const parsed = JSON.parse(rawInput);
return parsed;
} catch (e) {
return defaultValue;
} Rethrowing
Sometimes you want to catch, do something (log, clean up), and re-throw so the error continues up.
async function readConfig(path) {
try {
return await readFile(path);
} catch (e) {
console.error(`Failed to read ${path}:`, e.message);
throw e; // let the caller see the failure too
}
} Catching Specific Error Types
If you throw different error subclasses, you can branch on type:
function maybeFail() {
throw new TypeError("bad input");
}
try {
maybeFail();
} catch (e) {
if (e instanceof TypeError) {
console.log("type error:", e.message);
} else if (e instanceof RangeError) {
console.log("range error:", e.message);
} else {
throw e; // unknown error — let it propagate
}
} We’ll cover the built-in error types in the next lesson.
Optional catch Binding
You can leave out the (e) if you don’t need it.
try {
doSomething();
} catch {
// we don't care what the error was
showDefault();
} Up Next
Throwing errors yourself.
JavaScript throw →