229

Node.js from version 7 has async/await syntactic sugar for handling promises and now in my code the following warning comes up quite often:

(node:11057) UnhandledPromiseRejectionWarning: Unhandled promise 
rejection (rejection id: 1): ReferenceError: Error: Can't set headers 
after they are sent.
(node:11057) DeprecationWarning: Unhandled promise rejections are 
deprecated. In the future, promise rejections that are not handled 
will terminate the Node.js process with a non-zero exit code.

Unfortunately there's no reference to the line where the catch is missing. Is there any way to find it without checking every try/catch block?

mrts
  • 16,697
  • 8
  • 89
  • 72
user1658162
  • 2,721
  • 2
  • 19
  • 23
  • You could use the Bluebird promise library and it would likely give you a stack trace. – jfriend00 May 07 '17 at 17:47
  • 3
    Perhaps registering to Node's `unhandledRejection` event will help? See the [docs](https://nodejs.org/api/process.html#process_event_unhandledrejection). Your callback gets the `Error` object and the actual `Promise`, and I believe the `Error` object might hold a stack trace. – YSK May 07 '17 at 18:20
  • If the two previous comments don't help, then `Can't set headers after they are sent.` should give you a clue where in your code it could be happening (i.e. somewhere you are setting headers after the headers would've been already sent - presumably because of a failing in understanding asynchronous code, but that's a guess) – Jaromanda X May 07 '17 at 23:15
  • hi that messages helps for sure in finding where in the code the bug is, btw it's not as easy as knowing the line. – user1658162 May 09 '17 at 08:30
  • @jfriend00 I use the Bluebird library, and it doesn't help with this problem either. Still get those messages, without stack trace/line numbers. – Adam Reis Nov 02 '17 at 00:57
  • @AdamReis - If it is a Bluebird promise that has an unhandled rejection and the library is properly configured to show long reject traces, it will show you everything you want to see. But, if the promise is not a Bluebird promise, there's nothing Bluebird can do about that. If you want help with your particular situation, you'd have to write your own question and include the actual code involved. – jfriend00 Nov 02 '17 at 02:30
  • 1
    @jfriend00 It turns out it was a situation where an async function was throwing an error -- those internal Node promises for async functions don't use Bluebird, ever, so having Bluebird doesn't help in that scenario. – Adam Reis Nov 02 '17 at 07:13
  • I don't understand why this is not a default behaviour.. – Krishna Oct 11 '21 at 04:48

5 Answers5

370

listen unhandledRejection event of process.

process.on('unhandledRejection', (reason, p) => {
  console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
  // application specific logging, throwing an error, or other logic here
});
cuixiping
  • 24,167
  • 8
  • 82
  • 93
  • 49
    Logging `error.stack` (or in the above example `reason.stack`) gives you the full stack trace of the error. – Adam Reis Nov 02 '17 at 01:07
  • Thanks for putting `process.on` rather than `server.on` like in so many other examples I've found – keba Jan 16 '18 at 23:49
  • 12
    I wish I could say this worked, but it doesn't. I'm on Node 8.9.4. – ffxsam Feb 10 '18 at 16:40
  • 6
    I tried the above code and got undefined for both reason, and p? Any suggestions? " Unhandled Rejection at: Promise { state: 'rejected', reason: undefined } reason: undefined " – Jeremy Apr 21 '18 at 00:52
  • 1
    Tried this and it worked like a charm! Resolved my issue very quickly. – smb Sep 10 '18 at 20:04
  • 4
    I added this code to the top of my node `app.js` file and nothing is logged unfortunately. Node `v10.13.0`. – user1063287 Nov 13 '18 at 13:33
  • If this is not helpful because of for instance: `Unhandled Rejection at: Promise Promise { undefined } reason: undefined.` then you can cause an error in the callback function on purpose by calling for instance `reason.stack` knowing that `reason` is undefined. It will show a longer stack including the source of issue – Luke Mar 18 '20 at 10:36
  • 8
    Why the heck isn't this part of the default behavior of Node? I'm on v14.8.0 and it still shows the completely unhelpful messages even when I use `--trace-warnings`. – TaylanKammer Aug 24 '20 at 12:18
  • Or use console.log( { reason } ) to have a better output of the log – Royal Nov 25 '20 at 16:35
  • For those who are handling `unhandledRejection` in AWS Node.js Lambdas: adding `process.removeAllListeners('unhandledRejection');` to your function before using this solution allows you to handle this type of error. – Dustin M. Feb 25 '21 at 23:12
  • 2
    Where to add this code if I'm testing React app with Jest? – Kangur Aug 12 '21 at 12:25
87

The correct way to show a full stacktrace for unhandled ES6 Promise rejections, is to run Node.js with the --trace-warnings flag. This will show the full stacktrace for every warning, without having to intercept the rejection from within your own code. For example:

node --trace-warnings app.js

Ensure that the trace-warnings flag comes before the name of your .js file! Otherwise, the flag will be interpreted as an argument to your script, and it will be ignored by Node.js itself.

If you want to actually handle unhandled rejections (eg. by logging them), then you might want to use my unhandled-rejection module instead, which catches all the unhandled rejections for every major Promises implementation that supports it, with a single event handler.

That module supports Bluebird, ES6 Promises, Q, WhenJS, es6-promise, then/promise, and anything that conforms to any of the unhandled rejection specifications (full details in the documentation).

Sven Slootweg
  • 3,735
  • 3
  • 21
  • 29
  • 31
    Using node 7.8.0 and all this gives me is a stack trace for a bunch of internal node modules. (node:10372) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): undefined at emitWarning (internal/process/promises.js:59:21) at emitPendingUnhandledRejections (internal/process/promises.js:86:11) at process._tickDomainCallback (internal/process/next_tick.js:136:7) – Will Lovett Apr 06 '18 at 13:44
  • 7
    I don't see any output that shows where the unhandled promise issue is. – Jason Leach Sep 25 '18 at 17:26
  • 1
    I added this to `package.json` start script and nothing was logged unfortunately. Node `v10.13.0`. – user1063287 Nov 13 '18 at 13:37
  • 1
    @user1063287 Ensure that the flag is in the correct place in your command. I've just added an update to the answer, to emphasize that it needs to go *before* the script name. – Sven Slootweg Nov 14 '18 at 23:41
  • I just tried this, all I got was the stack trace of emitPendingUnhandledRejections, and I'm fairly sure the command was right, as I wasn't getting the stack trace of that before. Not sure this is helpful in these cases, but a fair try – Craig Brett May 02 '19 at 08:21
  • 2
    You're likely looking at the stack trace of the deprecation warning, not of the original unhandled error (which should be somewhere *above* the deprecation warning). – Sven Slootweg May 03 '19 at 12:12
  • If you are using ts-node then you have to set NODE_OPTIONS="--trace-warnings" to make it work – Luke Mar 18 '20 at 10:32
18

Logging with stack trace

If you are looking for more of a helpful error message. Try adding this to your node file. It should display the full stack trace where your crash is happening.

process.on('unhandledRejection', (error, p) => {
  console.log('=== UNHANDLED REJECTION ===');
  console.dir(error.stack);
});
Community
  • 1
  • 1
joshuakcockrell
  • 5,200
  • 2
  • 34
  • 47
  • The only functional difference is doing a console.dir on the error's stack property. Quite a difference in output compared to the accepted answer. – joshuakcockrell Feb 04 '20 at 20:14
  • 2
    I tried this but `error.stack` is `undefined` for me. :( node v14.15.4 – Wyck Apr 29 '21 at 19:43
  • 1
    This will only work when error is an instance of Error. It won't work if the promise is rejected with e.g. a string value: `throw "Some error"` – Michael2 May 18 '21 at 20:37
  • will this work if there is no crash? `DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.` It's not crashing *now* and I want to fix it before it has a chance to in the future. – Michael Jan 11 '23 at 22:11
11

This module allowed me to track down the culprit promise(s): https://www.npmjs.com/package/trace-unhandled

  1. Install

    npm i trace-unhandled
    
  2. Include in code

    require('trace-unhandled/register');
    
egekhter
  • 2,067
  • 2
  • 23
  • 32
0

@Jeremy I had same result, the reason variable provided by

process.on('unhandledRejection', (reason, p) => {});

wasn't defined and it take me time to figure out that there were promise rejection providing nothing in my code, typically:

new Promise((resolve reject) => {
  const client = net.connect(options)
  client.on("connect", () => {
    resolve()
  })
  client.on("error", () => {
    reject()
  })
})

the problem is that you reject with nothing, to get trace you have to provide error, like this

new Promise((resolve reject) => {
  const client = net.connect(options)
  client.on("connect", () => {
    resolve()
  })
  client.on("error", (err) => {
    reject(err)
  })
})

and if you don't have error you can provide one yourself, even if empty, it will give a stack trace

reject(new Error())

If you need to find where the error was throwed from, look in your code for Promise with empty rejection

DevTheJo
  • 2,179
  • 2
  • 21
  • 25
  • yeah i've got literally hundreds of async functions, and there isn't any quicker way to find it than try to track down *every* single place a promise is created, either explicitly or implicitly? – Michael Jan 11 '23 at 22:10