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 throughawait
working withthenables
(I think)Aggregate#exec()
is called internally byAggregate#then()
- Note that a callback is provided to
exec()
- Note that a callback is provided to
- A new
Promise
insideAggregate#exec()
is created, and will be rejected- This is the unhandled
Promise
, I believe.
- This is the unhandled
- Since a callback is provided to
Aggregate#exec()
fromAggregate#then()
, theError
insideAggregate#exec()
will be provided to the callback - Inside the callback in
Aggregate#then()
, a newly createdPromise
is rejected- I believe this
Promise
is handled as expected, since it is the return fromAggregate#then()
- I believe this
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();