Skip to content

JavaScript error“Uncaught (in promise)” and unhandled promise rejections

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

    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

    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

    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 : MDN: Window: unhandledrejection event; MDN: Using promises (error handling, returning promises); Node.js: Command-line API (--unhandled-rejections).

Rather hand it over?

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

Related: “Unexpected token < in JSON” · Supabase “Failed to fetch” in a published app · ECONNREFUSED: connection refused

Questions

More on this error

Is “Uncaught (in promise)” the real error?

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.

Should I just add .catch(() => {}) everywhere?

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.

Can you fix it for me?

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.

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.