JavaScript unhandledrejection Event Guide

This article provides an overview of the unhandledrejection event in JavaScript, explaining how JavaScript tracks uncaught asynchronous errors, how the event functions across browser and Node.js environments, and how developers can implement global listeners to prevent silent application failures and improve debugging.


What is the unhandledrejection Event?

In JavaScript, asynchronous operations are predominantly handled using Promises. A Promise exists in one of three states: pending, fulfilled, or rejected. When an asynchronous operation fails or an error is thrown inside a Promise chain, the Promise enters the rejected state.

If a rejected Promise lacks a rejection handler (such as a .catch() block or a wrapping try...catch in an async/await context), it becomes an unhandled rejection. When this occurs, the JavaScript runtime emits the unhandledrejection event on the global object to signal that an asynchronous error was not caught locally.


How JavaScript Monitors Uncaught Async Errors

Traditional error-handling mechanisms like window.onerror or standard try...catch blocks are designed to intercept synchronous runtime errors. Because Promise executions are deferred to the microtask queue, errors occurring inside asynchronous callbacks bypass synchronous call-stack error handlers.

To track these missed errors, modern JavaScript runtimes monitor the state of all Promises:

  1. Microtask Queue Evaluation: When a Promise rejects without an attached rejection callback, the runtime flags the Promise internally as unhandled.
  2. Event Loop Notification: At the end of the microtask checkpoint, if the Promise remains unhandled, the runtime dispatches the unhandledrejection event.
  3. Fallback Logging: If no global listener suppresses the default behavior, the runtime outputs an error message directly to the developer console.

Capturing Unhandled Rejections in the Browser

In browser environments, the unhandledrejection event is dispatched on the global window object. You can listen for it using window.addEventListener.

window.addEventListener('unhandledrejection', (event) => {
  console.warn('Unhandled Promise Rejection Detected:');
  console.error('Reason:', event.reason);
  console.log('Promise:', event.promise);

  // Prevent the default browser console error output
  event.preventDefault();
});

Event Object Properties


Capturing Unhandled Rejections in Node.js

Node.js provides a corresponding mechanism attached to the global process object via the unhandledRejection event.

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
  // Optional: Gracefully terminate the process or log to a monitoring service
});

Note: In modern Node.js versions, unhandled Promise rejections cause the process to terminate with a non-zero exit code if not handled.


The rejectionhandled Event

JavaScript also provides a complementary event called rejectionhandled. This event fires when a handler (such as .catch()) is attached to a rejected Promise after the unhandledrejection event has already fired.

This is useful in debugging environments or custom promise-tracking libraries to reconcile whether a previously flagged unhandled rejection was eventually addressed.

window.addEventListener('rejectionhandled', (event) => {
  console.log('Promise rejection was handled late:', event.promise);
});

Best Practices for Handling Async Errors