Promise.prototype.catch()

catch(onRejected)
Returns: Promise · Added in ves6 · Updated March 15, 2026 · Async APIs
promise async es6 javascript

The catch() method attaches a callback function that handles Promise rejection. It is essentially a shorthand for promise.then(null, onRejected) but is more readable and the preferred way to handle errors in Promise chains.

Syntax

promise.catch(onRejected)
promise.catch(reason => {
  // handle error
})

Parameters

ParameterTypeDefaultDescription
onRejectedfunctionundefinedCalled if the Promise is rejected. Receives the rejection reason (error) as an argument.

Return Value

A new Promise that:

  • Resolves with the return value of onRejected if it does not throw
  • Resolves with the original fulfillment value if the original Promise resolved
  • Rejects with the error thrown by onRejected if it throws

Examples

Basic Error Handling

fetch("/api/user")
  .then(response => response.json())
  .catch(error => {
    console.error("Failed to fetch user:", error.message);
  });

Chaining After Multiple Operations

fetch("/api/data")
  .then(data => validate(data))
  .then(processed => save(processed))
  .then(result => console.log("Success:", result))
  .catch(error => {
    console.error("Operation failed:", error.message);
  });

Recovering from Errors

fetch("/api/user")
  .then(response => response.json())
  .catch(error => {
    // Return default user on failure
    return { name: "Guest", id: 0 };
  })
  .then(user => {
    console.log("User:", user.name); // Works even if fetch failed
  });

Catching Specific Errors

async function getData() {
  try {
    const response = await fetch("/api/data");
    if (!response.ok) {
      throw new Error("HTTP " + response.status);
    }
    return await response.json();
  } catch (error) {
    if (error.message === "HTTP 404") {
      return { data: [] }; // Handle missing gracefully
    }
    if (error.message === "HTTP 500") {
      throw new Error("Server error - please try again later");
    }
    throw error;
  }
}

Short-circuiting a Chain

Promise.resolve(10)
  .then(x => {
    if (x > 5) throw new Error("Too big");
    return x;
  })
  .catch(error => {
    console.log(error.message); // "Too big"
    return 0; // Recover with default
  })
  .then(x => {
    console.log(x); // 0 - chain continues!
  });

Using with finally

fetch("/api/resource")
  .then(response => response.json())
  .catch(error => {
    console.error("Error:", error.message);
    return null;
  })
  .finally(() => {
    console.log("Request completed"); // Always runs
  });

Common Mistakes

Not Returning in catch

// WRONG: Lost error, but also breaks the chain
fetch("/api/data")
  .then(data => process(data))
  .catch(error => {
    console.error(error.message);
  })
  .then(result => {
    console.log(result); // undefined if error occurred!
  });

// CORRECT: Return a value or re-throw
fetch("/api/data")
  .then(data => process(data))
  .catch(error => {
    console.error(error.message);
    return null; // Recover with null
  })
  .then(result => {
    console.log(result); // null if error occurred, or actual result
  });

Swallowing Errors

// BAD: Silently ignoring errors
fetch("/api/data").catch(() => {});

// BETTER: At least log something
fetch("/api/data").catch(err => console.warn("Error:", err));

// BEST: Handle appropriately
fetch("/api/data")
  .then(data => render(data))
  .catch(error => showErrorToast(error.message));

Placing catch Before other then Handlers

// WRONG: catch might catch errors from later in the chain
fetch("/api/user")
  .catch(error => console.error(error))
  .then(user => user.name) // This can still throw!
  .then(name => console.log(name));

// CORRECT: catch at the end
fetch("/api/user")
  .then(user => user.name)
  .then(name => console.log(name))
  .catch(error => console.error(error));

See Also