Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

Why does a try/catch miss an error from a promise I did not await?

Quick answer

A normal try/catch catches errors thrown while its block is running. If you start a promise but do not await it, the block can finish before that promise rejects, so the later rejection is not caught there. Use await inside the try block, or attach .catch() to the promise. [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

Why doesn’t this catch the error from loadData()?

Geez

Calling loadData() starts a promise, but this code does not wait for that promise to settle. The try block can finish before the promise rejects; its later rejection is therefore not caught by this synchronous catch. [S1]

Minimal example:

function loadData() {
  return Promise.reject(new Error("Request failed"));
}

try {
  loadData(); // Promise is started, but not awaited
} catch (error) {
  console.error("Caught:", error.message);
}

Here, the catch does not handle the rejection. The key distinction is between an error thrown while the try block is executing and a promise rejection that happens later. Without await or a rejection handler, the promise’s eventual failure is not connected to that catch. [S1]

Follow-up question

What should I change if I want to handle the failure before continuing?

Geez

Make the surrounding function async, then await the promise inside the try block. A rejection from the awaited operation is handled as an error at that point, so the nearby catch can handle it. [S1]

async function showData() {
  try {
    const data = await loadData();
    console.log("Loaded:", data);
  } catch (error) {
    console.error("Could not load data:", error.message);
  }
}

showData();

Use this pattern when the next steps depend on the operation’s result, or when you want this function to finish handling the failure before it completes. The await must be inside the try block; putting it after the block would leave that rejection outside this particular catch. [S1] This example assumes loadData() returns a promise. Choose an error response that suits your application.

Follow-up question

What if I want to start the operation and handle its rejection without awaiting it?

Geez

Attach a .catch() handler to the promise chain. That gives the rejection a handler even though the surrounding code does not wait for the operation to finish. [S1]

function startLoading() {
  loadData()
    .then((data) => {
      console.log("Loaded:", data);
    })
    .catch((error) => {
      console.error("Could not load data:", error.message);
    });

  console.log("Started loading");
}

This is useful when the work can proceed independently of the next line. Keep the handler attached to the promise you start. A .catch() after .then() also handles a rejection from the returned chain, including one caused by the .then() callback. [S1] Choose what the handler should do, such as showing an error state, rather than swallowing the failure with an empty handler.

Follow-up question

How do I choose between those patterns, and what should I check if my catch still does not run?

Geez

Choose based on whether this function needs to wait. If later work needs the result, use await inside try. If the operation can run independently, attach .catch() to its promise chain. Both patterns connect a rejection handler to the promise; a bare call inside try does not. [S1]

For example, if a screen needs data before it can render that data, the awaited pattern makes the dependency explicit. If a background action can proceed while the caller does other work, the chain pattern can handle its own failure. These are illustrative choices, not rules for every application.

When checking a stubborn case, trace the exact promise returned by the operation: confirm that the await is inside the try, or that .catch() is attached to the promise or chain that can reject. Also check whether the operation returns a promise at all; this guide explains promise rejections, not every kind of error-handling case. [S1] The right user-facing recovery behavior depends on your program.

A practical checklist

  • Identify the promise-returning call that can reject.

  • If the next step needs its result, put await for that call inside the relevant try block.

  • If the operation can run independently, attach .catch() to its promise chain.

  • Choose a meaningful response for the error instead of leaving an empty handler.

  • Verify that the handler is attached to the promise or chain that can actually reject.

A few more useful answers

Does putting a promise call anywhere inside `try` make its rejection catchable?

No. A call without await can return a promise that rejects after the synchronous try block has finished. Await it inside the block or attach .catch() to the promise chain. [S1]

Can I use both `await` and `.catch()`?

Either can handle a rejection, but using both for the same operation may make the flow harder to follow. Choose the pattern that fits whether the surrounding function needs to wait for the result.

What does this guide not establish?

It explains how to connect a handler to a promise rejection. It does not establish which recovery behavior, logging approach, or user-facing message is appropriate for a particular application.

Sources

References used for this guide.

  1. [S1] Using promises — JavaScript | MDN
    developer.mozilla.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work