Handling those unhandled promise rejections with JS async/await
2020 update: I updated the guide to reflect how the message has changed in the more recent versions of Node.js and I also shortened the post. If you are looking for the old version, you can find it in the Internet Archive.
The Problem
If you are seeing this error:
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason “…”.] { code: ERR_UNHANDLED_REJECTION’ }
then what you are doing is likely this:
- You put your code inside an
async
function in order to useawait
calls - One of your
await
ed functions fails (i.e. rejects a promise) - You get the error
Another possibility is that you know you need to catch the rejected promise. In this case, what might be happening is this:
- You put your code inside an
async
function in order to useawait
calls - You put an
await
call inside a try/catch block or you use .catch() - The
await
ed function fails (i.e. rejects a promise) - You
throw
an error in the catch block to terminate the script - You get the error
In the first case, the key is this part of the error message:
… by rejecting a promise which was not handled with .catch().
The solution is simple, either use .catch() as suggested by the message:
await returnsPromise().catch(e => { console.log(e) })
Or use a try/catch block:
try {
await returnsPromise()
} catch (error) {
console.log('That did not go well.')
}
In the second case, the key part is this:
throwing inside of an async function without a catch block
Don’t throw
inside of an async
function without catching!
You may think that the warning is about the promise from the function you awaited and which failed, but actually it’s about the promise returned from the wrapper async function within which you are throwing an error. Async functions return promises implicitly.
Your options in this case are:
- adding
.catch()
to your wrapper function call (you don’t even really need the try/catch block inside the wrapper then)
(async function () {
try {
await returnsPromise()
} catch (error) {
console.log('That did not go well.')
throw error
}
})().catch( e => { console.error(e) })
- using
process.exit(1)
instead ofthrow
to terminate the script (lazy but efficient…)
(async function () {try {
await returnsPromise()
} catch (error) {
console.error(error)
process.exit(1)
}console.log('This will not be printed.');})()
Could it be cleaner?
If all you want is to terminate the script and display the error, you can simply get rid of the try/catch
block altogether. The catch at the end of the IIFE will take care of the rejected promise from returnsPromise
.
Again, this is a rather lazy solution which may backfire in a more complex script.
(async function () { await returnsPromise()
console.log('This will not be printed.');})().catch( e => { console.error(e) })
Do you always need to add the .catch() at the end of the IIFE?
Not if you catch and handle everything within the function.
If you want to terminate the script, you could catch the error and exit the process with process.exit(1) instead of using throw
, like this with .catch()
:
(async function () {await returnsPromise().catch((e) => { console.error(e); process.exit(1) })console.log('This will not be printed.');})()
or like this with try/catch
:
(async function () { try {
await returnsPromise()
} catch (error) {
console.error(error)
process.exit(1)
} console.log('This will not be printed.');})()
But this gets a little repetitive if you have multiple await
s in your script.
Finally, just to be clear, if you want the script to continue, a .catch() or try/catch without a throw will be just fine. You can execute later parts of the code conditionally. For example, result
below will be undefined
:
(async function () {var result = await returnsPromise().catch((e) => { console.error(e.message) })console.log( result ? 'This was a success! ' + result : 'This was a failure.' )})()