How to find which promises are unhandled in Node.js UnhandledPromiseRejectionWarning? - node.js

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?

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
});

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).

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);
});

This module allowed me to track down the culprit promise(s):
https://www.npmjs.com/package/trace-unhandled
Install
npm i trace-unhandled
Include in code
require('trace-unhandled/register');

#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

Related

Node logging unexpected UnhandledPromiseRejectionWarning

I have a piece of code that's causing Node to log UnhandledPromiseRejectionWarning. But I'm not sure why. Here's the code boiled down:
export class Hello {
async good(): Promise<string> {
let errorP = this.throwError();
let responseP = this.doSomething();
let [error, response] = await Promise.all([errorP, responseP]);
return response + '123';
}
async bad(): Promise<string> {
let errorP = this.throwError();
let responseP = this.doSomething();
let response = (await responseP) + '123';
let error = await errorP;
return response;
}
private async throwError(): Promise<string> {
await (new Promise(resolve => setTimeout(resolve, 1000)));
throw new Error('error');
}
private async doSomething(): Promise<string> {
await (new Promise(resolve => setTimeout(resolve, 1000)));
return 'something';
}
}
Calling try { await hello.bad(); } catch (err) {} causes node to log UnhandledPromiseRejectionWarning
Calling try { await hello.good(); } catch (err) {} does NOT log the warning
Full error:
(node:25960) UnhandledPromiseRejectionWarning: Error: error
at Hello.<anonymous> (C:\hello-service.ts:19:11)
at Generator.next (<anonymous>)
at fulfilled (C:\hello-service.ts:5:58)
at runNextTicks (internal/process/task_queues.js:58:5)
at listOnTimeout (internal/timers.js:523:9)
at processTimers (internal/timers.js:497:7)
(node:25960) UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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()
. To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:25960) [DEP0018] 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.
{"level":30,"time":1639745087914,"pid":25960,"hostname":"AZWP10801-12","reqId":"req-1","dd":{"trace_id":"2081604231398834164","span_id":"2081604231398834164","service":"#amerisave/example-service","version":"0.0.0"},"res":{"statu
sCode":200},"responseTime":1025.4359999895096,"msg":"request completed"}
(node:25960) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
Some dependency versions:
node ver. 14.16.1
ts-node-dev ver. 1.1.8
ts-node ver. 9.1.1
typescript ver. 4.5.2
Why is good good, but bad bad?
The problem in bad() is because the errorP promise rejects BEFORE you get to await errorP and thus it rejects when there is no reject handler for it in place. Nodejs detects that a promise rejected and your process gets back to the event loop and that rejected promise does not have a reject handler on it. That gets the "unhandled rejection" warning.
Notice here that while await errorP doesn't directly apply a reject handler, it does tie errorP to the parent async function which does have a reject handler on it, so the await errorP indirectly assigns reject handling to errorP. Whereas errorP by itself will just reject and not cause anything to happen to the parent async function. It will just be a variable containing a now rejected promise with no reject handler on it.
To take advantage of async automatic error propagation of rejected promises, you have to await that promise.
Nodejs doesn't know you're going to add the await in the future with code that will execute some time in the future, so it will report the unhandled rejection.
Code becomes subject to these types of errors where you put a promise into a variable and you have no reject handler on that promise of any kind and then you go await on some other promise BEFORE you ever put any sort of reject handler on that previous promise. The promise is just sitting there with no error handling on it. If, due to the timing of things, it happens to reject in that state, you will get the warning. The usual solutions are:
Immediately put error handling on the promise so it's never left sitting by itself.
Don't create the promise until you're ready to use it however you're going to use it with appropriate error handling (with a .then().catch() or in a Promise.all().catch() or in an await or whatever).
Don't await other promises while a promise is sitting in a variable without any reject handling.
I find that if I can avoid putting a promise with no handling on it into a variable at all and rather just create the promise right into the circumstance where it's going to be monitored for completion and error, you don't even have to generally think about this issue.
FYI, you can illustrate the same general concept of a promise rejecting before you add a reject handler in a simpler manner here if you run this in nodejs:
function bad(t) {
return new Promise((resolve, reject) => {
setTimeout(reject, t);
});
}
const b = bad(500);
// this timer will fire after bad() rejects
setTimeout(() => {
b.catch(err => {
console.log("caught b rejection");
})
}, 600);
You will get the "uncaught rejection" error because when the promise rejects, it does not yet have a .catch() handler. Your code has this same issue (though obscured a little more) because the reject handler comes from the await and the async function and the try/catch the caller of the async function is using.
Here's a hypothesis (that can be experimentally proven).
The difference in behavior between good and bad can be explained by the order of awaits.
In bad you're awaiting on throwError after you have awaited on doSomething, while in good, you're awaiting on Promise.all, which will not return until both are fullfilled or at least one is rejected (which will be the case here).
So in bad, the throwing is happening outside of await, and your catch is not triggered, and it is caught internally by node.
If you change your bad so that you await on throwError first, then your catch will get triggered:
async bad(): Promise<string> {
let errorP = this.throwError();
let responseP = this.doSomething();
let error = await errorP;
let response = (await responseP) + '123';
return response;
}

Why is UnhandledPromiseRejectionWarning only appearing in production (Google Cloud Run)?

I am having a very hard time trying to solve a few functions that work perfectly fine on my laptop development environment (even if I run it with NODE_ENV=production) but when I deploy it to Google Cloud Run I get errors such as
Default
2021-12-15T12:16:35.016406Z at TCP.done (_tls_wrap.js:564:7)
Default
2021-12-15T12:16:35.016414Z(Use `node --trace-warnings ...` to show where the warning was created)
Default
2021-12-15T12:16:35.016426Z(node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
Default
2021-12-15T12:16:35.116284Z(node:1) [DEP0018] 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.
Default
2021-12-15T12:16:35.116931Z(node:1) UnhandledPromiseRejectionWarning: Error: socket hang up
Default
2021-12-15T12:16:35.116954Z at connResetException (internal/errors.js:639:14)
Default
2021-12-15T12:16:35.116969Z at TLSSocket.socketCloseListener (_http_client.js:449:25)
Default
2021-12-15T12:16:35.116979Z at TLSSocket.emit (events.js:412:35)
Default
2021-12-15T12:16:35.116989Z at net.js:686:12
Default
2021-12-15T12:16:35.116999Z at TCP.done (_tls_wrap.js:564:7)
causing my script to halt. In my local version it continues without stopping. It is very frustrating as I need to upload the code to Cloud Run all the time in order to test my code. I am testing it blindly as the errors do not appear in my development version.
I am quite new to Node.js and are constantly having a hard time with .then(), chaining .then(), promises, using resolve() so the code is likely to be wrong.
My questions here:
Why is this error only appearing on Cloud Run?
How can I configure my local computer the same way so it shows the same warnings and errors - so I can troubleshoot this?
My code (simplified):
function createOrUpdateSpotterData(data) {
// NOTE: Data comes from API and not from DB, thus no info such as twitter_status
// TODO: Separate into a create or update, in order to not check twitter ID every time.
return new Promise( async function (resolve) {
var twitterId = await twitterApi.getTwitterIdFromUsername(cleanedUsername)
data['twitter_numeric'] = twitterId
resolve(data)
}).then( (data) => {
db.collection('spotters').doc(data.marker).set(data)
}).catch( (error) => { console.log(error) })
}
async function updateSpottersJsonToDb() {
console.log("> updateSpottersJsonToDb")
return new Promise( (resolve, reject) => {
readStorageData(remote_files['spotters'])
.then( async (spotterListJson) => {
var spotterList = JSON.parse(spotterListJson)
await spotterList.forEach((spotter) => {
createOrUpdateSpotterData(spotter)
})
})
.then(() => {
resolve()
})
.catch((error) => {console.error(error)})
}).catch((error) => {console.error(error)})
It is quite embarrasing to admit but I was a bit overwhelmed by the quantity of this 'error message' that I could not see the forest for the trees. It was after all just a warning and by filtering the logs (on Google Cloud Logs) on std.out I realized this was not causing any of the issues I was having in my application.
The cause of this issue however was that I was not catching errors in my Promises correctly, which is really another issue (being discussed here).
If you find this through Google later on, it could be good to know that you can surpress this warning with process.on().

ECONNREFUSED error when loading a TensorFlow frozen model from node.js

I was trying to load a TensorFlow fronzen model from a url that points to not existing resource to test my code robustness. However, even though I have set a catch, I am not able to manage a ECONNREFUSED that is raised internally by the function tf.loadFrozenModel.
Is there any possible mitigation to this issue? This is for me a critical problem, since it stops the execution of nodejs.
Here is the code where the error is generated.
global.fetch = require("node-fetch");
const tf = require("#tensorflow/tfjs");
require("#tensorflow/tfjs-node");
class TFModel {
...
loadFzModel(modelUrl, modelWeigths) {
return tf.loadFrozenModel(modelUrl, modelWeigths)
.then((mod) => {
this.arch = mod;
})
.catch((err) => {
console.log("Error downloading the model!");
});
}
...
}
Here instead are the errors I am getting:
UnhandledPromiseRejectionWarning: Error: http://localhost:30000/webModel/tensorflowjs_model.pb not found. FetchError: request to http://localhost:30000/webModel/tensorflowjs_model.pb failed, reason: connect ECONNREFUSED 127.0.0.1:30000
at BrowserHTTPRequest.<anonymous> (.../node_modules/#tensorflow/tfjs-core/dist/io/browser_http.js:128:31)
at step (.../node_modules/#tensorflow/tfjs-core/dist/io/browser_http.js:32:23)
at Object.throw (.../node_modules/#tensorflow/tfjs-core/dist/io/browser_http.js:13:53)
at rejected (.../node_modules/#tensorflow/tfjs-core/dist/io/browser_http.js:5:65)
at process.internalTickCallback (internal/process/next_tick.js:77:7)
(node:23291) UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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(). (rejection id: 1)
(node:23291) [DEP0018] 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.
Note: the code works if modelUrl and modelWeights are valid url pointing to existing resources.
Node-2: the code is executed as part of a custom block for Node-Red.
If you don't find any other solution you can catch the error on the top level like this:
process.on('uncaughtException', function (err) {
console.error(err);
});
In there you can get more specific to only catch your specific error.
This is in the process of being addressed at https://github.com/tensorflow/tfjs-core/pull/1455.

How to track down the source of UnhandledPromiseRejectionWarning in node 8.2.1 [duplicate]

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?
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
});
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).
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);
});
This module allowed me to track down the culprit promise(s):
https://www.npmjs.com/package/trace-unhandled
Install
npm i trace-unhandled
Include in code
require('trace-unhandled/register');
#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

Promise remains unresolved even after handling all cases

I have been writing on a Discord.JS Bot for quite a bit now, and every now and then it seems to throw me a random error / warning in console after executing one of its chat commands
(specifically !clear).
Now, as I already stated, the message I get in my console is a warning, not an actual error, so that's not the main problem I have;
My problem lies in the execution of the command on Discord's side: Because of the unresolved, rejected promise, it will not execute !clear at all, leaving all messages including the command itself behind. Here's a snippet of my code:
if (member.hasPermission("MANAGE_MESSAGES")) {
channel.fetchMessages({ limit: 100 })
.then(messages => {
console.log(`Deleting ${messages.size} messages...`);
channel.bulkDelete(messages).then(res => {}, err => {});
channel.sendEmbed({
// Success Message
}).then(msg => msg.delete(10000), err => console.log(err));
}, err => { console.log(err) })
} else {
channel.sendEmbed({
// Permission Message
}).then(msg => msg.delete(10000), err => { console.log(err) });
}
As you can see, I resolved both the success and the failure state of every Promise, yet I will still see the following warning in console:
(node:14768) Error: Bad Request Sometimes also throws Not Found
-- Stack Trace that only includes internal Node.JS errors --
(node:14768) 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.
-- More Stack Trace not regarding any of my own code --
If any of you need additional code provided to answer the question, feel free to ask me and I will do so. Also, I can't +rep answers yet, but I always appreciate 'em :)
You aren't handling the msg.delete(10000) rejection. You should handle it like this:
channel.sendEmbed({
// Success Message
}).then(msg => msg.delete(10000)).catch(err => console.log(err));

Resources