Course contentsShow
← Node.js
Lesson 1418 of 1,98013. Error Handling and Resilient SystemsFree lesson

The Risks of Floating Promises

You will identify and remediate 'floating' promises that fail without being captured in a monitoring chain.

In this lesson, you will learn how to identify "floating" promises and why they are a silent threat to the stability of your Node.js services.

What is a Floating Promise?

A floating promise is a promise that is created but not explicitly handled—meaning there is no .catch() attached, and it is not await-ed. In Node.js, these "float" in the background, untethered from your error-handling logic.

If a promise is floating and its operation fails (e.g., a database write or an external API call), the rejection remains unhandled. In older versions of Node.js, this could crash your entire process. In modern versions, it triggers an unhandledRejection event, which can lead to "silent failures" where your system state becomes inconsistent without you ever knowing an error occurred.

The Anatomy of the Risk

Consider this common pattern in an API endpoint:

// A risky implementation
app.post('/log-activity', (req, res) => {
  // This promise is floating! 
  // If this fails, we have no idea why.
  analytics.track(req.body); 
  
  res.status(202).send('Accepted');
});

Because analytics.track() is not awaited or caught, any error inside that function disappears into the ether. You lose the ability to log the failure, retry the operation, or even notify your monitoring system. Furthermore, because you aren't waiting for the outcome, your application might try to shut down or perform other tasks, leaving these background operations in a race condition.

How to Remediate

To stop promises from floating, you must always bridge them to your error-handling chain. If an operation is "fire-and-forget," you should explicitly attach a handler:

app.post('/log-activity', (req, res) => {
  // Explicitly attaching a catch handler
  analytics.track(req.body).catch(err => {
    logger.error('Background tracking failed:', { error: err });
  });

  res.status(202).send('Accepted');
});

By adding the .catch(), you bring the error into your observability pipeline, preventing the "silent failure" trap. If the operation is critical, you should await it so the failure propagates correctly to your middleware or global error handlers.

Key Takeaway: A promise without an await or a .catch() is a blind spot in your system; always tether your promises to an error handler to ensure failures are captured and logged.