Error Handling — try/catch/finally + Custom Errors
Error Handling — try/catch/finally + Custom Errors
🎯 After reading this lesson
Once you finish this lesson, you'll be able to confidently do the following three things.
- ▸✅ try / catch / finally + Error.cause (ES2022)
- ▸✅ Separate domain exceptions with custom error classes
- ▸✅ Error handling in async functions + unhandledrejection
Keep the learning objectives as a checklist — close the lesson once you can answer all of them.
try / catch / finally — Basic Structure
The Simplest try-catch
When an error is thrown, the code above immediately jumps to the catch block. If nothing is thrown, catch is skipped.
finally — Runs Regardless of Success or Failure
The standard pattern for resource cleanup (database connections, files, locks).
4 Properties of the Error Object
4 Common Error Types
- ▸TypeError —
null.foo,undefined()— the most frequently seen error - ▸ReferenceError — referencing a variable that doesn't exist, like
consle.log - ▸SyntaxError — the syntax itself is broken (usually caught at build time)
- ▸RangeError — negative array length or infinite recursion
If you paste the error message directly into an AI, it will pinpoint the cause in most cases — that's the core of "saving debugging tokens."
Custom Error Classes — Separation by Domain
Why Custom Errors Are Needed
If you throw everything as a generic Error — branching by cause inside catch becomes difficult. Separate them with custom classes:
Branching Logic
Branch by type using instanceof. The intent of your code becomes clear.
ES2022 cause — Error Chaining
Wraps a new error while preserving the original cause. The original stack trace remains in the logs, making debugging easier.
Error Handling in async / await — 3 Standard Patterns
Pattern 1 — try/catch in async
The most commonly used form. fetch does not reject on 4xx/5xx — you must check res.ok or res.status manually.
Pattern 2 — Promise.catch()
Used in places that aren't async functions (event handlers, etc.). Can also be mixed with async/await.
Pattern 3 — Let the Error Propagate Up
Domain logic functions don't use try-catch. Responsibility is delegated to the top-level caller (controller or UI component).
A Common Pitfall — await Inside forEach
forEach does not recognize Promises. Switch to for...of instead:
React Error Boundary — UI Error Handling
Catches errors during rendering to prevent the entire app from crashing. try-catch cannot catch rendering errors.
🤖 Try asking AI like this
- ▸"Add a res.ok check and try-catch to this fetch code"
- ▸"Branch this error into NotFoundError and ValidationError handling"
- ▸"The await inside forEach isn't working — convert it to for...of"