Skip to content
Latchkey

Node.js "ERR_UNHANDLED_REJECTION" - Fix Unhandled Promise Rejections

A promise rejected and no .catch() or surrounding try/await handled it. On modern Node the default mode is throw, so the process prints ERR_UNHANDLED_REJECTION and exits non-zero, failing the job.

What this error means

A script that uses async code aborts with UnhandledPromiseRejection and ERR_UNHANDLED_REJECTION, then exits non-zero. The stack often points at a library call whose rejection you never awaited.

Node output
node:internal/process/promises:288
            triggerUncaughtException(err, true /* fromPromise */);
            ^
[UnhandledPromiseRejection: This error originated either by throwing inside of an
async function without a catch block, or by rejecting a promise which was not
handled with .catch(). The promise rejected with the reason "TypeError: ...".]
  code: 'ERR_UNHANDLED_REJECTION'

Common causes

A rejected promise with no handler

An async call rejected (a failed fetch, a thrown error inside an async function) and the calling code did not await it inside a try, nor attach a .catch(). Nothing absorbed the rejection.

Default unhandled-rejection mode is "throw"

Since Node 15, an unhandled rejection terminates the process by default. Code written against old Node (which only warned) now hard-fails in CI.

How to fix it

Handle the rejection at its source

Await the call inside a try/catch, or attach a .catch(). This is the real fix - the crash is a genuine unhandled error, not a transient blip.

app.mjs
try {
  await doWork();
} catch (err) {
  console.error('doWork failed:', err);
  process.exitCode = 1;
}

Add a last-resort process handler

A top-level handler lets you log context before exiting, instead of the bare internal stack. It is a safety net, not a substitute for handling the rejection.

app.mjs
process.on('unhandledRejection', (reason) => {
  console.error('Unhandled rejection:', reason);
  process.exit(1);
});

How to prevent it

  • Always await promises inside try/catch or attach .catch().
  • Enable no-floating-promises (typescript-eslint) to catch unawaited promises at lint time.
  • Treat the crash as a real bug - it will not pass on retry.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →