bluebird: returning Promise.reject in async function results in Unhandled rejection error message

  1. What version of bluebird is the issue happening on? 3.5.0

  2. What platform and version? (For example Node.js 0.12 or Google Chrome 32) Node.js 8.0.0 on Ubuntu

  3. Did this issue happen with earlier version of bluebird? Yes

When returning Promise.reject in async function, an error is printed on the console “Unhandled rejection” even the promise is caught. It doesn’t print out the error if native Promise is used.

const Promise = require("bluebird");

async function WaitAsync(){
    return Promise.reject(new Error("reject"));
}

Promise.resolve().then(() => {
    return WaitAsync();
}).catch(err => {
    console.log("caught: ", err);
});

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 14
  • Comments: 21 (2 by maintainers)

Most upvoted comments

I believe I’m encountering another instance of this issue. In fact I was going to post a new issue before I saw this one. Here’s my description:

This issue is a bit difficult to grasp, so I’ll do my best to explain with examples. It seems that when using Promise.using() with an async function, awaiting on already rejected promises always outputs an Unhandled rejection when using Bluebird’s Promise.reject(), but not when using native Promise.reject() or throw in an async function.

These code snippets are runnable directly on node 8 (tested on v8.1.0) with Bluebird v3.5.0 (and earlier).

First, let’s start from a problematic example.

// Failing example 1

const Promise = require('bluebird')

Promise.using(Promise.resolve(), async () => {
  await Promise.reject(new Error('foo'))
})
.catch(() => console.log('OK'))

This snippet, when run, produces the following output:

Unhandled rejection Error: foo
    at Promise.using (<redacted>/a.js:4:24)
    at tryCatcher (<redacted>/node_modules/bluebird/js/release/util.js:16:23)
    at <redacted>/node_modules/bluebird/js/release/using.js:185:26
    at tryCatcher (<redacted>/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (<redacted>/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (<redacted>/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (<redacted>/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (<redacted>/node_modules/bluebird/js/release/promise.js:693:18)
    at Promise._fulfill (<redacted>/node_modules/bluebird/js/release/promise.js:638:18)
    at PromiseArray._resolve (<redacted>/node_modules/bluebird/js/release/promise_array.js:126:19)
    at PromiseArray._promiseFulfilled (<redacted>/node_modules/bluebird/js/release/promise_array.js:144:14)
    at Promise._settlePromise (<redacted>/node_modules/bluebird/js/release/promise.js:574:26)
    at Promise._settlePromise0 (<redacted>/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (<redacted>/node_modules/bluebird/js/release/promise.js:693:18)
    at Async._drainQueue (<redacted>/node_modules/bluebird/js/release/async.js:133:16)
    at Async._drainQueues (<redacted>/node_modules/bluebird/js/release/async.js:143:10)

OK

As you can see, we have an unhandled rejection error yet we did handle the error, and no clear way of rewriting the snippet so that the unhandled rejection would not occur.

Confusingly, if we reject by using native promises, we do not get the unhandled rejection. In this snippet, we only use .using() from Bluebird.

// Working example 1

const Bluebird = require('bluebird')

Bluebird.using(Promise.resolve(), async () => {
  await Promise.reject(new Error('foo'))
})
.catch(() => console.log('OK'))

This outputs simply:

OK

Similarly, this also prints the warning:

// Failing example 2

const Promise = require('bluebird')

const reject = () => Promise.reject(new Error('foo'))

Promise.using(Promise.resolve(), async () => {
  await reject()
})
.catch(() => console.log('OK'))
Unhandled rejection Error: foo
    at reject (<redacted>/a.js:3:37)
    at Promise.using (<redacted>/a.js:6:9)
    at tryCatcher (<redacted>/node_modules/bluebird/js/release/util.js:16:23)
    at <redacted>/node_modules/bluebird/js/release/using.js:185:26
    at tryCatcher (<redacted>/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (<redacted>/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (<redacted>/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (<redacted>/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (<redacted>/node_modules/bluebird/js/release/promise.js:693:18)
    at Promise._fulfill (<redacted>/node_modules/bluebird/js/release/promise.js:638:18)
    at PromiseArray._resolve (<redacted>/node_modules/bluebird/js/release/promise_array.js:126:19)
    at PromiseArray._promiseFulfilled (<redacted>/node_modules/bluebird/js/release/promise_array.js:144:14)
    at Promise._settlePromise (<redacted>/node_modules/bluebird/js/release/promise.js:574:26)
    at Promise._settlePromise0 (<redacted>/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (<redacted>/node_modules/bluebird/js/release/promise.js:693:18)
    at Async._drainQueue (<redacted>/node_modules/bluebird/js/release/async.js:133:16)

OK

However, this one doesn’t:

// Working example 2

const Bluebird = require('bluebird')

const reject = () => Promise.reject(new Error('foo'))

Bluebird.using(Promise.resolve(), async () => {
  await reject()
})
.catch(() => console.log('OK'))
OK

And neither does this one:

// Working example 3

const Promise = require('bluebird')

const reject = async () => {
  throw new Error('foo')
}

Promise.using(Promise.resolve(), async () => {
  await reject()
})
.catch(() => console.log('OK'))
OK

With async/await translated to plain promises, the failing examples also work without issue:

// Previously failing example 1 (working w/o async)

const Promise = require('bluebird')

Promise.using(Promise.resolve(), () => {
  return Promise.reject(new Error('foo'))
})
.catch(() => console.log('OK'))
OK
// Previously failing example 2 (working w/o async)

const Promise = require('bluebird')

const reject = () => Promise.reject(new Error('foo'))

Promise.using(Promise.resolve(), reject)
.catch(() => console.log('OK'))
OK

This leads me to believe that there may perhaps be a bug in Bluebird, causing a rejected bluebird-promise returned by an async function to be handled asynchronously.

Thoughts?

We just got bit by this issue. We’ve just recently moved to node@8.2.1 and are using async/await extensively, along with bluebird.

I see @suguru03 has a PR to fix it, is that going to be merged soon?

This change causes resource leak check to fail in our tests

@adamreisnz I believe there’s no way to alter the type of Promise returned from async functions, it is always internal Promise

const SavePromise = global.Promise;
global.Promise = String;
async function f1() {
    return "ok"
}

const p = f1();
console.log( "f1 returned: ", p );
console.log( "p instanceof global.Promise: ", p instanceof global.Promise );
console.log( "p instanceof SavePromise: ", p instanceof SavePromise );
f1 returned:  Promise { 'ok' }
p instanceof global.Promise:  false
p instanceof SavePromise:  true