JavaScript Try Catch
Handle errors safely with try-catch patterns for async code, error propagation, global handlers, and JSON parsing.
TL;DR
- 01Wrap risky code in
tryblocks to intercept runtime errors. - 02Use
try-catchwithawaitto catch rejected promises cleanly. - 03Catch unhandled rejections globally using
windowerror event listeners.
Tips
- 01Always include a
finallyblock when managing resources like file handles or database connections to guarantee cleanup. - 02Use the
causeoption in theErrorconstructor to preserve the original traceback when wrapping error exceptions.
Warnings
- 01Avoid using bare
catchblocks that swallow errors silently, as this makes diagnosing application bugs very difficult. - 02Never wrap entire script bodies in a single
try-catchstatement because it masks syntax errors during load time.
Async Error Handling
try...catchCatches errors thrown during asynchronous operations when combined with await.
async function loadUser(id) {
try {
const res = await fetch(`/users/${id}`);
return await res.json();
} catch (err) {
console.error(err.message);
return null;
}
}Multiple awaitsGroups multiple sequential asynchronous actions into a single error handler block.
try {
const user = await getUser();
const orders = await getOrders(user.id);
console.log(orders);
} catch (err) {
console.error("Chain failed", err);
}finallyGuarantees resource cleanup or UI state resets regardless of try-catch outcomes.
setLoading(true);
try {
await saveData(form);
} catch (err) {
showError(err.message);
} finally {
setLoading(false);
}Error Propagation
throwRethrows a caught error to escalate it up the application call stack.
function processData(raw) {
try {
return JSON.parse(raw);
} catch (err) {
console.warn("Parsing failed", err);
throw err;
}
}error causeAttaches a low-level error cause when throwing a new high-level wrapper error.
try {
await db.query(sql);
} catch (cause) {
throw new Error("DB failed", { cause });
}cause propertyRetrieves the nested origin error object from the cause property during inspection.
try {
await loadData();
} catch (err) {
console.log(err.cause.message);
}Global Handlers
window.onerrorListens for unhandled synchronous execution errors globally across browser page environments.
window.onerror = (msg, src, line) => {
console.error(`Error: ${msg} at ${src}:${line}`);
return true; // intercept
};unhandledrejectionCatches any promise rejections that lack a corresponding catch block handler.
window.addEventListener("unhandledrejection", e => {
console.error("Unhandled:", e.reason);
e.preventDefault();
});uncaughtExceptionListens for terminal exceptions globally in Node.js process environments.
process.on("uncaughtException", err => {
console.error("Fatal error occurred:", err);
process.exit(1);
});Safe Parsing Patterns
JSON.parse()Safeguards JSON parsing routines by catching syntax validation failures.
function parseJSON(str, fallback = null) {
try {
return JSON.parse(str);
} catch {
return fallback;
}
}Optional catchOmits the catch block error variable binding when the object is unused.
try {
data = JSON.parse(raw);
} catch {
data = {};
}localStorage guardProtects localStorage reads which can throw errors in private browser modes.
function getStored(key) {
try {
return JSON.parse(localStorage.getItem(key));
} catch {
return null;
}
}Inspecting Errors
error.stackProvides trace details including file locations and execution call history.
try {
riskyOperation();
} catch (err) {
console.error(err.stack);
}cause tracingRecursively traverses a wrapped error cause chain to resolve the root error.
try {
await saveOrder(order);
} catch (err) {
let current = err;
while (current?.cause) {
current = current.cause;
}
console.log("Root:", current.message);
}AggregateErrorCollects multiple individual promise errors during collective parallel operations.
try {
await Promise.any([checkA(), checkB()]);
} catch (err) {
err.errors.forEach(e => console.log(e.message));
}In Practice
Wraps network and response parsing exceptions inside a custom error class to maintain context and track causes.
- 01Extend the standard error class to declare a custom
NetworkErrorconstructor. - 02Perform a fetch request inside a synchronous-like
tryblock. - 03Throw a high-level error if the server response status is not successful.
- 04Catch any network or parsing failure inside the
catchblock handler. - 05Rethrow a custom
NetworkErrorwrapping the original error as the cause.
class NetworkError extends Error {
constructor(msg, cause) {
super(msg, { cause });
this.name = "NetworkError";
}
}
async function fetchJson(url) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(res.statusText);
return await res.json();
} catch (err) {
throw new NetworkError("Fetch failed", err);
}
}FAQ
Use try-catch blocks for unpredictable operations like JSON.parse() or network requests. For values you can verify beforehand using variables or properties, choose conditional null checks instead. Conditional checks are faster and cleaner.
Inspect the caught exception using the instanceof operator inside the catch block. For example, check if (err instanceof TypeError) to handle specific issues. Always rethrow unknown exceptions with throw err to prevent swallowing bugs.
Yes, the finally block is guaranteed to execute even if the try block calls return. It runs right before the function exits. This makes it perfect for resetting loading states and releasing resource handles.
Extend the built-in Error class: class ValidationError extends Error { ... }. Set this.name inside the constructor method. Extending the class ensures you maintain the correct stack trace for debugging.
Yes, wrapping an await statement inside a try-catch block catches promise rejections. This synchronous-like syntax avoids trailing .catch() chains. It simplifies error handling across multiple sequential asynchronous requests.