October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk4 min

How to Handle Errors in Nested Promises Without Swallowing Them

A log-only catch can turn a rejection into fulfillment. Learn when to return a fallback, when to rethrow, and how to keep nested async work connected to its caller.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To keep a nested promise’s failure visible, return or await that promise so it joins the caller’s chain. If you catch an error only to log it or clean up, throw it again; a .catch() callback that returns normally turns the result of .catch() into a fulfilled promise.

Why can .catch() make a promise resolve?

.catch(onRejected) returns a new promise. If onRejected returns normally, that new promise fulfills with the returned value—even when the promise before .catch() was rejected. A log-only handler usually returns undefined, so downstream code continues as fulfilled with undefined.

As an Amazon Associate I earn from qualifying purchases.

operation()
  .catch((error) => {
    console.error(error);
    // Returns normally: the next promise fulfills with undefined.
  })
  .then(() => continueWork());

This is not an automatic bug: a catch that returns a usable fallback is a recovery. It is a problem when the intent was only to record the error while leaving the operation failed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose whether to recover or preserve the failure

Recover locally with a meaningful fallback

When the current layer can genuinely recover, return a value that downstream code can use. Later chain steps receive that value as success, rather than the original rejection. This behavior follows from the Promise catch() reference on MDN.

return loadSettings()
  .catch((error) => {
    logError(error);
    return defaultSettings; // Deliberate recovery; downstream receives a value.
  });

Log, clean up, or add context while keeping the rejection

If a caller still needs to know the operation failed, throw after the local action. The promise returned by the catch then rejects, allowing the next error boundary to decide what to do. MDN describes the rule this way: “Therefore, if an error must be handled immediately, but we want to maintain the error state down the chain, we must throw an error of some type in the rejection handler.” See the MDN Promise reference.

return loadSettings()
  .catch((error) => {
    logError(error);
    throw error;
  });

When adding context, preserve the original error as the cause if the runtime and codebase support that pattern:

return loadSettings()
  .catch((error) => {
    throw new Error("Could not load settings", { cause: error });
  });

Return nested promises so the caller can observe them

A promise returned from a .then() callback is joined to the promise produced by that .then(). Its eventual fulfillment or rejection therefore travels through the resulting chain. MDN’s guide to using promises recommends keeping simple chains flat; nesting can also change which catch handles a failure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Detached inner work

function loadProfile(id) {
  return fetchProfile(id)
    .then((profile) => enrichProfile(profile))
    .catch((error) => {
      logError(error);
      throw error; // The caller still sees a rejection.
    });
}

// The inner promise is not returned, so this chain does not wait for it.
outer().then(() => {
  inner();
});

Joined inner work

outer().then(() => {
  return inner();
});

Returning the inner promise makes its result part of the outer chain. If it rejects, a catch on that resulting chain can handle it. Without the return, the outer chain can fulfill before the inner work finishes, and its catch is not automatically attached to the separate promise.

Use await inside the try that should catch the failure

In an async function, await turns a promise rejection into a thrown reason at that point in the function. Put the await inside the try whose catch should own the failure:

async function loadProfileForPage(id) {
  try {
    return await loadProfile(id);
  } catch (error) {
    showProfileError(error);
    throw error; // Keep it failed if this layer cannot recover.
  }
}

The await makes the local catch’s scope explicit. If the function does no work in the try after calling loadProfile and simply returns its promise, it can omit await and write return loadProfile(id); the returned promise still adopts that promise’s outcome. By contrast, try { doAsyncWork(); } catch (error) { ... } catches a synchronous throw made while invoking the function, not a later rejection from its unawaited promise. The MDN await reference explains how rejection is raised at the await expression.

Check the common traps

  • Log-only catch: returning normally fulfills the promise returned by .catch(). Throw again if propagation is required.
  • Fallback catch: returning a fallback intentionally recovers; later steps receive that fallback as success.
  • Inner promise not returned: return it, or await it in an appropriate async function, so the caller’s chain can observe its outcome.
  • Unawaited call inside try: the try block does not wait for a later rejection. Await the operation or attach and route a rejection handler.
  • Async callback passed to an API: throwing inside the callback rejects the callback’s own promise. If the API does not use or await that promise, the outer operation is not necessarily joined to it; follow the API’s callback contract and route failure to the boundary that owns it.
  • Catch on a different branch: a catch handles rejections that reach the promise it is attached to. Join separate work into the chain or handle that branch explicitly.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Handle errors at the application boundary, not as a host-level workaround

Browsers expose unhandledrejection when a rejected promise has no handler, and rejectionhandled if a handler is attached later. These events report promise handling status; they do not substitute for attaching a handler where the application knows how to recover or report the failure. See MDN’s promise guide.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Node.js v26.10.0 documents process events for unhandled and later-handled rejections. Its documented default --unhandled-rejections mode is throw, under which an unhandled rejection is raised as an uncaught exception. Runtime behavior depends on the Node.js version and flags; consult the Node.js process documentation for the version you deploy. Do not use process-level rejection or uncaught-exception handlers as a substitute for connecting the promise to its intended error owner.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.