JavaScript Try Catch

Handle errors safely with try-catch patterns for async code, error propagation, global handlers, and JSON parsing.

TL;DR

  1. 01Wrap risky code in try blocks to intercept runtime errors.
  2. 02Use try-catch with await to catch rejected promises cleanly.
  3. 03Catch unhandled rejections globally using window error event listeners.

Tips

  1. 01Always include a finally block when managing resources like file handles or database connections to guarantee cleanup.
  2. 02Use the cause option in the Error constructor to preserve the original traceback when wrapping error exceptions.

Warnings

  1. 01Avoid using bare catch blocks that swallow errors silently, as this makes diagnosing application bugs very difficult.
  2. 02Never wrap entire script bodies in a single try-catch statement because it masks syntax errors during load time.

Async Error Handling

    try...catch

    Catches 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 awaits

    Groups 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);
    }
    finally

    Guarantees 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

    throw

    Rethrows 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 cause

    Attaches 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 property

    Retrieves the nested origin error object from the cause property during inspection.

    try {
      await loadData();
    } catch (err) {
      console.log(err.cause.message);
    }

Global Handlers

    window.onerror

    Listens 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
    };
    unhandledrejection

    Catches any promise rejections that lack a corresponding catch block handler.

    window.addEventListener("unhandledrejection", e => {
      console.error("Unhandled:", e.reason);
      e.preventDefault();
    });
    uncaughtException

    Listens 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 catch

    Omits the catch block error variable binding when the object is unused.

    try {
      data = JSON.parse(raw);
    } catch {
      data = {};
    }
    localStorage guard

    Protects 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.stack

    Provides trace details including file locations and execution call history.

    try {
      riskyOperation();
    } catch (err) {
      console.error(err.stack);
    }
    cause tracing

    Recursively 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);
    }
    AggregateError

    Collects 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

FAQ