Node.js error chaining not propagating to calling function - node.js

I have a Nodejs cron job that runs every 5 seconds:
cron.schedule("*/5 * * * * * *", async function() { //Every 5 seconds
try{
await cleanUpDatabase()
}
catch(err){
console.log(err)
console.trace(err.lineNumber)
}
});
async function cleanUpDatabase(){
let pool = await connection;
pool.query(`SELECT * FROM table1`)
}
I do not have a table named table1 in my database. I want the second function to produce an error
But from my understanding of try catch block since the function that is calling the cleanUpDatabase function has catch statement it should catch the error even though there is no try catch on cleanUpDatabase function. However it does not catch it
In my express application I have a function that displays all unhandled rejection:
process.on('unhandledRejection', (error, p) => { //I added this so that I can console log the unhandled rejection and where it is coming from. Before this I would just get UnhandledPromiseRejectionWarning: Unhandled promise rejection without knowing which promise was not handled
console.log('=== UNHANDLED REJECTION ==='); // Not good to have unhandled promise rejection in code. This will just help me locate it incase here is one
console.dir(error.stack);
});
This function is catching the error. Which means that the error is not handled. The error that I am getting is:
=== UNHANDLED REJECTION === "RequestError: Invalid object name 'table1'.\n"
Why is the parent function not able to handle the error?
Edit 1:
Even this does not work. I'm really confused now
async function cleanUpDatabase(){
try{
let pool = await connection;
pool.query(`SELECT * FROM table1`)
}
catch(err){
throw err;
}
}

Try this:
function cleanUpDatabase() {
return new Promise(async (resolve, reject) => {
let pool = await connection;
pool.query(`SELECT * FROM table1`);
resolve();
}).catch((error) => {
console.log(error);
});
}

Related

Node process exits even after error handling

I have something like this written in nodejs
const someOtherOperation = async (message) => {
try {
await doSomeIoOperation(message);
} catch (err) {
something
throw Error("Error doing someOtherOperation");
} finally {
await someCleanup();
}
}
const someOperation = async (message) => {
// something else
await someOtherOperation(message);
// something else
}
const main = async () => {
let messagePromises = []
let messages = await getMessages(); // fetching message from a message broker
for (let message of messages) {
messagePromises.push({ id: message.id, promise: someOperation(message) });
}
for (let messagePromise of messagePromises) {
try {
await messagePromise.promise;
} catch (err) {
console.log(err);
}
}
}
The expected behaviour is the for loop with try catch should not end even if there is a error in one of the promises.
What is happening is my process is ending abruptly when i get an error in someotherOperation method , i do not understand i have a try catch at the main loop and any error propagating from the innermost function should be caught in the for loop in main function but it isn't getting caught somehow and the function just ends abruptly
Node.js detection of unhandled rejection is incorrect. There are specific spots in the life cycle of a rejected promise where the engine checks to see if there's a handler and it does not always wait until the last possible moment so it can miss places that we add a handler.
In my code the place where i create a bunch of promises without any error/rejection handlers
messagePromises.push({ id: message.id, promise: someOperation(message) });
when i do the first await on the first promise it return rejected but during this time consider other promises have also been rejected , the Nodejs engine checks if there is a handler for these rejected promises and throws and error if no handler is present. So even though i add a try catch for these promises in a hope to handle the rejections the nodejs engine has already run a check for a handler and not getting one decided to throw a unhandled promise rejection.
To get around my style of code what i did was
const main = async () => {
let messagePromises = []
let messages = await getMessages(); // fetching message from a message broker
for (let message of messages) {
messagePromises.push({ id: message.id,
promise: someOperation(message).then(data=>[data,null]).catch(err=>[null,err]);
}
for (let messagePromise of messagePromises) {
const [data,err] = await messagePromise .promise;
if(err) {
//some error handling code here.
}
}
}
This is a pretty weird behaviour for such a mature language.

How to add catch while returning promise

Initially i was getting UnhandledPromiseRejectionWarning: TypeError: Cannot read property "distance" of undefined. to solve that error i have added a if condition with reject. now getting this error
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). Any solutions ?
here is my code
export function fun1(): Promise < survayResult > {
return new Promise((resolve, reject) => {
//..........
//..........
surveyDistance.forEach((result) => {
//
})
if(condition) {
reject(new Error("something went erong"));
return
}
let totalDistance = surveyDistance[0].distance;
// ...
//...
resolve("with some data");
})
}
I think you're over-thinking it, the promise should resolve or reject, they don't have built in error handling support. However in this case the error message is probably a good thing as it is telling you that the arguments (surveyDistance) are not what you expected. I won't comment on good or bad practices because I don't know your code base but you should hesitate to use try/catch unless there really is something that could go wrong (in my experience).
export function fun1(): Promise < survayResult > {
return new Promise((resolve, reject) => {
try {
//..........
//..........
surveyDistance.forEach((result) => {
//
})
if(condition) {
reject(new Error("something went erong"));
return
}
let totalDistance = surveyDistance[0].distance;
// ...
//...
resolve("with some data");
} catch (e) {
reject("Your error message:" + e);
}
})
}

Why doesn't multilayered async functions catch error thrown at lowest level in node?

I'm trying to test a failure mode of some mailing code which at the lowest level may throw an error. All the layers between the test and the function which throws are all async and use await on the functions below them. At the top level (also in an async function I have a try catch block. However node is throwing an unhandled promise exception before the error propages to this level.
My test code looks like this
beforeEach(function() {
//set default values - tests can change them
this.reasons = '';
this.reschedules = 0;
this.params.cid = 35124;
this.startTest = async () => {
/* this.confirmation is an async function under test,
this.mailer is a mock mailer with an async "send" method
which will throw an error in the correct test */
const doner = this.confirmation(this.mailer);
// ..other actions related to mocking database access made by confirmation
await doner;
return this.mailer.maildata; //provide info on parameters passed to this.mailer
};
});
it('Failure to send is reported', async function() {
this.mailer.sendResolve = false; //tell mock mailer to fail send request
try {
await this.startTest();
expect(true).to.be.false;
} catch(err) {
expect(err).to.be.instanceOf(Error);
}
});
the mock mailer is a bit like this
class Mailer {
constructor(user,params){
...
}
...
async send(subject, to, cc, bcc) {
this.maildata.subject = subject;
if (to !== undefined) this.maildata.to = to;
if (cc !== undefined) this.maildata.cc = cc;
if (bcc !== undefined) this.maildata.bcc = bcc;
if (!this.sendResolve) throw new Error('Test Error');
}
...
}
and a summary of the code under test
module.exports = async function(mailer) {
//get confirm data from database
const cData = await confirm(mailer.params.cid, mailer.db);
if (cData.count > 0) {
// ... format the email message and build it into maildata
await mailer.send(
subject,
emailAddress,
null,
process.env.PAS_MAIL_FROM,
{
pid:cData.pid,
type: 'confirmation',
extra: `Calendar ID ${mailer.params.cid} with procedure ${cData.procedure}`
}
);
debug('message sent, update the database');
await mailer.db.exec(async connection => {
...
});
debug('success');
} else {
debug('invalid calendarid');
throw new Error('Invalid Calendar ID');
}
};
As can be seen the call path from the async send function which throws back up the stack to the try {}catch(){} are all async functions. But when I run this test node outputs an unhandled promise rejection.
I've tried using the visual studio code debugger to single step through this, I get a bit lost caught in the machinery which wraps async functions to turn them into promises providers. As far as I can see, one layer of error is handled correctly and then fails at the next layer up.
Does this mean that every async function must have a try catch block to catch and rethrow any error? I can't find any explanation that says I have to do that.
To answer your question:
Does this mean that every async function must have a try catch block to catch and rethrow any error?
Errors propogate up through await-ed calls like you expected:
const assert = require('assert');
const outer = async () => {
await middle();
}
const middle = async () => {
await inner();
}
const inner = async () => {
throw new Error('something bad happened');
}
it('should catch the error', async () => {
let errorMessage;
try {
await outer();
}
catch (err) {
errorMessage = err.message;
}
assert(errorMessage === 'something bad happened'); // Success!
});
...so no, you don't need a try / catch block at every level.
Tracking down unhandled Promise rejections
I can't see exactly where the await chain might be broken in the code from your sample, but to help track down unhandled Promise rejections you can add a process handler for the unhandledRejection event and look at the logged Promise to see where the rejection began and track backwards through the call stack from there:
const assert = require('assert');
const outer = async () => {
await middle();
}
const middle = async () => {
inner(); // <= this will cause an Unhandled Rejection
}
const inner = async () => {
throw new Error('something bad happened');
}
it('should catch the error', async () => {
let errorMessage;
try {
await outer();
}
catch (err) {
errorMessage = err.message;
}
assert(errorMessage === undefined); // Success! (broken await chain)
})
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at:', p);
console.log('reason:', reason);
});
...which in this case logs:
Unhandled Rejection at: Promise {
<rejected> Error: something bad happened
at inner (.../code.test.js:12:9)
at inner (.../code.test.js:8:3)
at middle (.../code.test.js:4:9) // <= this is the broken link
at Context.outer (.../code.test.js:18:11)
at callFn (...\node_modules\mocha\lib\runnable.js:387:21)
...
...which points us to the Error thrown in inner, and by tracing up the chain we find middle to be the broken link.

Azure Function automatic retry on failure UnhandledPromiseRejectionWarning

const fetch = require('node-fetch');
let url = 'something.com';
module.exports = function(context) {
let a = fetch(url)
a.then(res => {
if(res.status!=200) throw new Error(res.statusText)
else{
context.done(null, res.body);
}
});
a.catch(err => {
console.log(err)
throw new Error(err)
});
};
I have a durable function that calls an activity function like above. I have set automatic retry on failure on this activity function. To retry the function needs to get an error.
So In get request I want to throw an error when i get response like 404 or something similar. But when i throw from catch block i get an error like below
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().
function pauses there and stops execution.I have to manually stop and start the execution. How can i handle this so that the function retries?
Your code branches.
Ignoring the detail, what you have is :
let a = <Promise>; // root
a.then(...); // branch_1
a.catch(...); // branch_2
So whereas you catch errors arising in a, any error arising in branch 1 will be uncaught. Hence the warning
Compare that with :
let a = <Promise>; // root
a.then(...).catch(...); // branch
or
<Promise>.then(...).catch(...); // no assignment necessary
So, you might write :
module.exports = function(context) {
return fetch(url)
.then(res => {
if(res.status!=200) {
throw new Error(res.statusText);
} else {
context.done(null, res.body);
}
})
.catch(err => {
console.log(err)
throw new Error(err)
});
};
Alternatively, depending on the required division of responsibilities between module and caller(s) ...
module.exports = function(context) {
return fetch(url)
.then(res => {
if(res.status!=200) {
throw new Error(res.statusText);
} else {
return res;
}
});
};
... and call .context.done(null, res.body); in a .then() callback in the caller.
In both cases, with return included, then the caller will need to catch errors otherwise you will again get an unhandled error warning.
Found that with the use of async/await this problem goes away and the function re-try after exception is thrown.
const fetch = require('node-fetch');
let url = 'something.com';
module.exports = async function(context) {
let res = await fetch(url)
if(res.status!=200) throw new Error(res.statusText);
else return res.body;
};

unhandled promise rejection warning (despite having catch block)

I seem to have a chained catch block for handling promise rejection (and it does catch the rejection ok) but I still see the warning about unhandled promise rejection. What am I not getting right here?
Promise.resolve(req.query.request_token)
.then(function(rt) {
request_token = rt+'2'
return kc.generateSession(request_token, api_secret)
})
.then(function(resp) {
console.log(resp)
return kc.setAccessToken(resp.access_token)
})
.then(() => console.log(kc))
.catch(err => console.error(err))
So, I tried recreating this example and seeing if something is wrong with your promise chain, but I don't think there is because my below example works fine, ie: I tried throwing errors, and rejecting each one of the mock functions but no matter what I do in the mock functions, the 'this happens here' still gets printed.
function testResolve() {
return new Promise((res, rej) => res(1))
}
function generateSession(a, b) {
return new Promise((res, rej) => {
console.log(a)
console.log(b)
res(`a: ${a}, b: ${b}`)
})
}
function setAccessToken(token) {
return new Promise((res, rej) => {
res(1)
})
}
Promise.resolve(testResolve())
.then(function(rt) {
var request_token = rt+'2'
return generateSession(request_token, 'a1')
})
.then(function(resp) {
console.log(resp)
return setAccessToken(resp.access_token)
})
.then(() => console.log(x))
.catch(err => {
console.log('this happens here')
console.error(err)
})
I would check the unhandled promise rejection message and see the line number that it gives you, and look at the filename where the error occurred. It's possible that there was an unhandled rejection inside code somewhere else, because as far as I know this promise chain should catch any errors that happen anywhere in the chain.

Resources