Node.js UnhandledPromiseRejectionWarning even after catching it Node.js UnhandledPromiseRejectionWarning even after catching it mongoose mongoose

Node.js UnhandledPromiseRejectionWarning even after catching it


There definitely appears to be something strange with how mongoose aggregate plays with async/await. Seems like a bug, to me. If it is, it should definitely be reported to mongoose.

Thankfully, there is an easy work around:

const followers = await User.aggregate(aggregateQuery).exec();

Adding the explicit .exec() allows me to catch the aggregate pipeline error as expected.


I think the underlying issue adding to the confusion here is that there is an additional Promise floating around which is being rejected and not handled. Because technically, you are handling the expected rejection here correctly. Otherwise you wouldn't see that Printing error = ... being logged.

Here's what I believe is happening --

  • You await User.aggregate()
  • Aggregate#then() is called through await working with thenables (I think)
  • Aggregate#exec() is called internally by Aggregate#then()
  • A new Promise inside Aggregate#exec() is created, and will be rejected
    • This is the unhandled Promise, I believe.
  • Since a callback is provided to Aggregate#exec() from Aggregate#then(), the Error inside Aggregate#exec() will be provided to the callback
  • Inside the callback in Aggregate#then(), a newly created Promise is rejected
    • I believe this Promise is handled as expected, since it is the return from Aggregate#then()

I think I can confirm my suspicions by commenting out this line in the mongoose Aggregate definition. This will prevent the unhandled rejection handler from being hit. Not that I'm suggesting to do that, by the way. That's just additional evidence, not a solution, since now I just have an unrejected Promise floating around.


Here is a minimal-ish way to reproduce the uncaught rejection in a self contained bit of code, to be run with node --harmony-async-await (tested on node v7.2.1)

const mongoose = require('mongoose');mongoose.Promise = global.Promise;mongoose.connect('mongodb://localhost/temp');const userSchema = new mongoose.Schema({  name: 'string'});const User = mongoose.model('User', userSchema);async function run() {  try {    await User.aggregate([]);  }  catch (err) {    console.log('caught expected error:', err);  }}run();