# “Uncaught (in promise)” and unhandled promise rejections

> Uncaught (in promise) in the console, or a Node.js crash on an unhandled rejection. Add try/catch around await, return promises, and catch at the top.

Canonical: https://app369.com/fix/uncaught-in-promise/
Last updated: 2026-11-07

The browser console shows Uncaught (in promise) followed by the real error (often TypeError: Failed to fetch or an API error), or a Node.js process exits on an unhandled promise rejection. Something async failed and no code was waiting to handle the failure. The message after “(in promise)” is the actual problem; the prefix only says nobody caught it.

**Short answer**

Fix the error that follows the prefix first, then make sure every async call has a handler: wrap await in try/catch, add .catch() to promise chains, and return promises from then() callbacks so their errors reach the catch at the end. In React, catch inside the async function you call from useEffect or an event handler and set an error state. In Node.js 15 and later an unhandled rejection crashes the process by default, so add the missing catch rather than silencing it.

## **Why the rejection is not handled, and the fix**

1. 01 ### **An await without try/catch** An async function that awaits a failing call rejects its own promise. If the caller does not await it inside try/catch (or call .catch()), the rejection is unhandled. Event handlers and useEffect callbacks are common places for this, because nothing awaits them.```
useEffect(() => {
  async function load() {
    try {
      const res = await fetch('/api/me');
      if (!res.ok) throw new Error(\`HTTP ${res.status}\`);
      setUser(await res.json());
    } catch (err) {
      setError(err.message);
    }
  }
  load();
}, []);
```
2. 02 ### **A promise started but not returned**
  > “It is important to always return promises from then callbacks, even if the promise always resolves to undefined.” [**MDN, Using promises**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) Inside a then() callback, a promise that is started but not returned is cut off from the chain, so the .catch() at the end never sees its error. Return it, or switch to async/await so the flow is easier to follow.```
// 🔴 the inner promise is floating
save().then(() => { sendEmail(); }).catch(report);

// ✅ returned, so its error reaches catch
save().then(() => sendEmail()).catch(report);
```
3. 03 ### **Node.js now crashes on unhandled rejections**
  > “v15.0.0 Changed default mode to throw.” [**Node.js, Command-line API: --unhandled-rejections**](https://nodejs.org/api/cli.html) Since Node.js 15, an unhandled rejection is raised as an uncaught exception and stops the process, where older versions only printed a warning. Code that “worked” on Node 14 can start crashing after an upgrade. Add the missing catch; use a process-level handler only to log and exit cleanly, not to keep running in an unknown state.```
process.on('unhandledRejection', (reason) => {
  console.error('Unhandled rejection:', reason);
  process.exit(1);
});
```
4. 04 ### **Errors you want reported, not hidden**
  > “The unhandledrejection event is sent to the global scope of a script when a JavaScript Promise that has no rejection handler is rejected” [**MDN, Window: unhandledrejection event**](https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event) A global unhandledrejection listener in the browser is useful for sending errors to your monitoring tool, but it does not fix the code path that failed. Use it for reporting, and still handle errors where the user needs feedback, such as a failed save. In Chrome, Uncaught (in promise) Error: A listener indicated an asynchronous response... comes from a browser extension, not your site: test in a window without extensions.```
window.addEventListener('unhandledrejection', (event) => {
  sendToMonitoring(event.reason);
});
```

Checked against the vendor’s docs on November 7, 2026: [MDN: Window: unhandledrejection event](https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event); [MDN: Using promises (error handling, returning promises)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises); [Node.js: Command-line API (--unhandled-rejections)](https://nodejs.org/api/cli.html).

## **Rather hand it over?**

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

- [from $450**App Fix Ticket**One reproducible bug in an existing mobile or web app, diagnosed and fixed.**See scope and start**](https://app369.com/fixed-price/flutter-firebase-bug-fix/)
- [from $450**AI-Built App Rescue**A Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.****No issues worth fixing, no charge.**** If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.**See scope and start**](https://app369.com/fixed-price/ai-app-rescue/)

Related: [**“Unexpected token < in JSON”**](https://app369.com/fix/unexpected-token-in-json/) · [**Supabase “Failed to fetch” in a published app**](https://app369.com/fix/supabase-failed-to-fetch-published-app/) · [**ECONNREFUSED: connection refused**](https://app369.com/fix/econnrefused-connection-refused/)

Questions

## **More on this error**

<details>

<summary>**Is “Uncaught (in promise)” the real error? **+</summary>

No. It is a prefix that says nothing handled the rejection. The real error follows it, such as TypeError: Failed to fetch or a message from your API. Fix that, and add a handler so the next failure shows the user a message instead of a silent console error.

</details>

<details>

<summary>**Should I just add .catch(() => {}) everywhere? **+</summary>

No. An empty catch hides failures, so users see nothing happen and you never hear about it. Catch where you can do something useful (show an error, retry, roll back) and report the rest.

</details>

<details>

<summary>**Can you fix it for me? **+</summary>

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

</details>

A different error?

## **Describe it and get a written quote**

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.

[**Describe your problem **→](https://app369.com/contact/) [**All fixes**](https://app369.com/fix/)