🌱BeginnerControl Flow
Error Handling
try/catch/finally, throwing errors, and the built-in Error types for writing resilient code.
Overview
Errors are inevitable a network request fails, a user provides invalid input, or a calculation divides by zero. JavaScript provides `try/catch/finally` to handle these situations gracefully instead of crashing the program.
The `try` block contains code that might fail. If any statement inside it throws an error, JavaScript immediately jumps to the `catch` block, skipping the rest of `try`. The `catch` block receives the error object, which has a `message` and a `stack` trace. The optional `finally` block always runs after either `try` or `catch`, whether or not an error occurred useful for cleanup like closing connections.
You can throw your own errors with `throw new Error("message")` or use specific subtypes like `TypeError`, `RangeError`, and `ReferenceError`. You can also extend `Error` to create custom error classes for your application.
Key Points
- •`try { ... } catch (err) { ... }` the error object `err` has `err.message` and `err.stack`
- •`finally { ... }` runs regardless of success or failure; ideal for cleanup code
- •`throw expression` throws any value, but always throw `Error` objects for good stack traces
- •`new Error("message")` generic error; subtypes: `TypeError`, `RangeError`, `ReferenceError`, `SyntaxError`
- •`err instanceof TypeError` check the specific error type in catch
- •Extend `Error` to create custom error classes: `class ValidationError extends Error {}`
- •Errors in async code: `try/catch` works with `async/await`; `.catch()` handles Promise rejections
Examples
Basic try/catch/finally
function parseJSON(str) {
try {
const result = JSON.parse(str);
return result;
} catch (err) {
console.log("Parse failed:", err.message);
return null;
} finally {
console.log("parseJSON called"); // always runs
}
}
console.log(parseJSON('{"name":"Alice"}')); // {name: "Alice"}
console.log(parseJSON("invalid json")); // Parse failed: ... | nullThrowing and catching specific error types
function divide(a, b) {
if (typeof a !== "number" || typeof b !== "number") {
throw new TypeError("Both arguments must be numbers");
}
if (b === 0) {
throw new RangeError("Cannot divide by zero");
}
return a / b;
}
try {
console.log(divide(10, 2)); // 5
console.log(divide(10, 0)); // throws RangeError
} catch (err) {
if (err instanceof RangeError) {
console.log("Math error:", err.message);
} else if (err instanceof TypeError) {
console.log("Type error:", err.message);
} else {
throw err; // re-throw unknown errors
}
}Custom error class
class ValidationError extends Error {
constructor(field, message) {
super(message);
this.name = "ValidationError";
this.field = field;
}
}
function validateAge(age) {
if (typeof age !== "number") {
throw new ValidationError("age", "Age must be a number");
}
if (age < 0 || age > 150) {
throw new ValidationError("age", `Age ${age} is out of valid range`);
}
return true;
}
try {
validateAge(200);
} catch (err) {
if (err instanceof ValidationError) {
console.log(`Validation failed on '${err.field}': ${err.message}`);
}
}Common Mistakes
- Catching errors silently (`catch (err) {}`) always at least log the error so you know something went wrong
- Putting too much code in `try` keep it focused on the operation that might fail, not the whole function
- Not re-throwing unknown errors if you only handle specific error types, re-throw others: `else { throw err; }`
Note
Use `finally` for cleanup that must happen regardless of success or failure: closing a file, releasing a lock, hiding a loading spinner. If both `catch` and `finally` are present, `finally` runs after `catch` completes.
Exercises
Write JavaScript code to solve each exercise. Run it and check your output.
Loading editor…
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4