Azure Function automatic retry on failure UnhandledPromiseRejectionWarning - node.js

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

Related

Try catch block doesn`t catch error in node.js

class AuthController {
static methods = {
GET: {
'/auth/signup': {
func: AuthService.signUp,
response: (data, res) => {
res.statusCode = 200;
res.end(JSON.stringify(data));
},
},
},
};
static use(req, res) {
const route = this.methods[req.method][req.url];
if (!route) {
res.statusCode = 404;
res.end(JSON.stringify({ message: 'Not found 404!' }));
return;
}
try {
const data = JSON.parse(req?.body?.data || '{}');
const result = route.func({ ...data });
route.response(result, res);
} catch (err) {
console.log(err, 'here');
res.statusCode = err.statusCode || 500;
res.end(JSON.stringify(err.message));
}
}
}
class AuthService {
static async signUp({ login, password }) {
if (!login || !password) throw new BaseError(400, 'kl', 'Custom error');
}
}
It shows the error in console but try catch block doesn't see it.
Here is the traceback.
I don't know what the reason is because the function which throws error is inside of the block. Help please!
The trace back that you attached tells you exactly what the problem is and what you need to do:
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()
You can't catch an exception thrown by an async function with a try..catch block outside of that function, because script execution reaches the catch block before the async execution is finished. You therefor have to use .catch(..) instead:
const result = route.func({ ...data }).catch((err) => {
console.log("catched error: ", err);
});
I see one issue. You have declared signUp() to be async. That means it always returns a promise and it means that any throw operations inside it reject that promise that it returns (the exception doesn't propagate synchronously). But, when you attempt to call it here:
const result = route.func({ ...data });
You don't await it so when signUp() rejects, the promise goes into result, but nobody ever handles the fact that the promise rejected and you get UnhandlePromiseRejectionWarning from the system.
I can't see the whole overall design (of all the other routes), but perhaps you just need to add await to this:
const result = await route.func({ ...data });
And, you would have to make .use() be async also.
Or, if signUp() doesn't actually need to be async, then just remove the async from its declaration and the throw will be synchronous (instead of being turned into a rejected promise) and your try/catch will catch it then.

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.

how to handle errors in multiple async await whle promise rejected without using try catch blocks?

i am new to express and was making user signup api. In this i need to hash user password and generate a random string. these both things are done by aync/await.If one promise got rejected then it returns the response in error handling but shows me warning for unhandled rejected promise.
methods.signup = async (req,res,next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(config.errorCodes.validation).json({errors:errors.array()});
}
let payload = req.body;
var newUser = new User({
username:payload.username,
fullname:payload.fullname,
email: payload.email,
password: payload.password,
urltoken:{
token:null
},
actStatus: 0,
provider: 'email',
role:1
});
let hash = commonMethods.generatePasswordHash(payload.password);
let token = commonMethods.createRandomString();
let [a,b] = await Promise.all([hash,token]).catch(err=>{
res.status(400).json({status:false,msg:"error in promise"})
});
newUser.password = a;
newUser.urltoken.token = b;
newUser.save(function(err){
if(err) {
return res.status(config.errorCodes.success).json({ status:false
,msg:"Error in saving userdata. Please try again",err});
}
else {
commonMethods.sendMail(newUser);
return res.status(config.errorCodes.success).json({"status":true,
"msg":"Registered succesfully. Please Check your Email to verify
your account.",newUser})
}
});
}
and promise are -
commonMethods.createRandomString = function(password){
return new Promise(function(resolve, reject) {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var string_length = 25;
var randomstring = '';
for (var i=0; i<string_length; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum,rnum+1);
}
reject(randomstring);
// resolve(randomstring);
})
}
which is always rejected for creating error.
Here is get below 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: 2)
(node:15172) [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.
how to catch these errors without using try catch or then for promises and keeping my code simple and easy to read.
The problem is the lines
let [a,b] = await Promise.all([hash,token]).catch(err=>{
res.status(400).json({status:false,msg:"error in promise"})
});
If either hash or token throw, then the .catch function is entered, but the catch function doesn't return anything: so, after the await and catch have finished, the interpreter will see something like
let [a,b] = undefined
which won't work (undefined is not iterable), so the whole .signup method rejects. You need some way for the rest of the signup function to know to stop executing if there was an error in hash or token, maybe something like
const result = await Promise.all([hash,token]).catch(err=>{
res.status(400).json({status:false,msg:"error in promise"})
});
if (!result) return;
let [a,b] = result;
You say you don't want to use try/catch, but the logic might be a bit easier to follow that way, since you could return immediately from inside the catch:
let a, b;
try {
([a, b] = await Promise.all([hash,token]));
} catch(e) {
return res.status(400).json({status:false,msg:"error in promise"});
}
Coding Convention Suggestion for API
1) There are two ways to handle errors neatly. The first one is by wrapping all the async routes with a try catch block which will handle both synchronous and asynchronous code errors.
Async/Await
methods.foo = async(req, res, next) => {
try {
// assuming bizz is a utility analogous to createRandomString
// and returns a promise object which will either get resolved or rejected
// if resolved the response will end in this case and end the response
// if rejected, await will throw an error which will be caught by the catch block
var bar = await bizz(); // Asynchronous code(which will wait for a promise to be resolved)
// {... DO SYNCHRONOUS STUFF ...}
// if suppose there is an error in your synchronous code,
// then it will throw an error which also will be caught by the catch block
res.end();
} catch (err) {
// {... DO WHAT YOU NEED TO WITH THE ERROR CAUGHT BY EITHER Asynchronous OR Synchronous part of the method ...}
console.log(err);
res.end(err);
}
}
2) The second way is to have a middleware that wraps all the routes so that rewriting try catch for all the routes is avoided. Here both synchronous and asynchronous errors will be handled by the .catch() part in the async Middleware.
Using Async Await Middleware
const asyncMiddleware = (asyncFunction) => {
return (req, res, next) => {
Promise.resolve(asyncFunction(req, res, next))
.catch((err) => {
// DO STUFF WITH ERROR
res.end(err);
});
}
};
methods.foo = asyncMiddleware((req, res, next) => {
// assuming bizz is a utility analogous to createRandomString
// and returns a promise object which will either get resolved or rejected
// if resolved the response will end in this case and end the response
// if rejected, await will throw an error which will be caught by asyncMiddleware
var bar = await bizz(); // Asynchronous code(which will wait for a promise to be resolved)
// {... DO SYNCHRONOUS STUFF ...}
// if suppose there is an error in your synchronous code
// then it will throw an error which also will be caught by asyncMiddleware
res.end();
});

Firebase Auth: Invalid email address crashing node.js (Express) app

I'm having a weird issue here. Simple code:
router.post("/resetpassword/:email", async (req, res) => {
try {
var auth = firebase.auth();
await auth.sendPasswordResetEmail(req.params.email);
res.sendStatus(200);
} catch (e) {
console.log(e);
res.sendStatus(400);
}
});
This code works fine if
The email is valid and known
The email is unknown (I get an exception with a status code that I can handle)
But if the email is malformatted, I also get an exception with status code, but then my Express application crashes after having invoked the catch block. The HTTP 400 is sent to the client, but after that, my app is dead.
Here's the console output. The first block if for an unknown email address, the second for a malformatted one.
{ [Error: There is no user record corresponding to this identifier. The user may have been deleted.]
code: 'auth/user-not-found',
message: 'There is no user record corresponding to this identifier. The user may have been deleted.' }
{ [Error: The email address is badly formatted.]
code: 'auth/invalid-email',
message: 'The email address is badly formatted.' }
[...]\node_modules\firebase\auth-node.js:39
h.send=function(a){if(a)if("string"==typeof a)this.sa.send(a);else throw Error("Only string data is supported");else this.sa.send()};h.abort=function(){this.sa.abort()};h.setRequestHeader=function(){};h.He=function(){this.status=200;this.responseText=this.sa.responseText;Ic(this,4)};h.Jd=function(){this.status=500;this.responseText="";Ic(this,4)};h.Je=function(){this.Jd()};h.Ie=function(){this.status=200;Ic(this,1)};var Ic=function(a,b){a.readyState=b;if(a.onreadystatechange)a.onreadystatechange()};var Jc=function(a,b,c){this.Ue=c;this.we=a;this.kf=b;this.oc=0;this.gc=null};Jc.prototype.get=function(){var a;0<this.oc?(this.oc--,a=this.gc,this.gc=a.next,a.next=null):a=this.we();return a};Jc.prototype.put=function(a){this.kf(a);this.oc<this.Ue&&(this.oc++,a.next=this.gc,this.gc=a)};var Kc=function(a){l.setTimeout(function(){throw a;},0)},Lc,Mc=function(){var a=l.MessageChannel;"undefined"===typeof a&&"undefined"!==ty
Error: The email address is badly formatted.
[nodemon] app crashed - waiting for file changes before starting...
I currently assume this is a stupid mistake from my side (I'm fairly new to node/Express). Any idea what I am missing here?
I came across this error myself while working with firebase.auth() myself, and is very peculiar indeed. What I figured is that firebase.auth() seems to use it's own Promise variant, and it is going to crash your app if that promise does not catch the error, or even throw the error in the .then/.catch chained to it.
here is a small function I made that makes sure it turns the rejected promise into a resolved promise, before again turning it into a rejected promise. This prevented my app from crashing, but I couldn't find an easier way.
let firebase_auth_wrap = async (promise) => {
let rejected = Symbol();
let value_or_error = await promise.catch((error) => {
return { [rejected]: true, error: error };
});
if (value_or_error[rejected]) {
throw value_or_error.error;
} else {
return value_or_error;
}
}
...
let { uid } = await firebase_auth_wrap(firebase.auth().signInWithEmailAndPassword(email, password));
Hope it works for you too, and let me know if it could be done more intuitively :)
When you run this:
await auth.sendPasswordResetEmail(req.params.email);
inside of a try/catch block then you handle two things:
The auth.sendPasswordResetEmail() throwing an exception immediately
The auth.sendPasswordResetEmail() rejecting the returned promise
What you don't handle is when in some other part of your code something happens and thrown an exception that is not caught by anything.
Of course it's impossible to tell you where it happened in your case when you post an example of an error message pointing to a line with thousands of characters but here is a simple example.
In this example you will catch the rejected promise:
let f = () => new Promise((res, rej) => {
rej(new Error('Error'));
});
(async () => {
try {
let x = await f();
console.log('Returned value:', x);
} catch (e) {
console.log('Handled error:', e.message);
}
})();
In this example you will catch the thrown exception:
let f = () => new Promise((res, rej) => {
throw new Error('Error');
});
(async () => {
try {
let x = await f();
console.log('Returned value:', x);
} catch (e) {
console.log('Handled error:', e.message);
}
})();
In this example you will handle the rejection that happened in a different tick of the event loop:
let f = () => new Promise((res, rej) => {
setImmediate(() => rej(new Error('Error')));
});
(async () => {
try {
let x = await f();
console.log('Returned value:', x);
} catch (e) {
console.log('Handled error:', e.message);
}
})();
But in this example you will not handle the exception thrown on a different tick of the event loop:
let f = () => new Promise((res, rej) => {
setImmediate(() => { throw new Error('Error'); });
});
(async () => {
try {
let x = await f();
console.log('Returned value:', x);
} catch (e) {
console.log('Handled error:', e.message);
}
})();
Something like that must be happening in your case.

How to return response to client in the middle of Promise?

For example,
Comments.findOne({user: req.user.id}).exce()
.then(function(comment) {
if(comment) {
// how to make this return immediately and break the rest then?
return res.json({error: 'Already commented'});
} else {
return Posts.findOne({postId: req.params.id}).exec();
}
})
.then(function(post) {
if(post) {
var comment = new Comment({user: req.user.id, data: req.body.comment})
return {post: post, comment: comment.save()};
} else {
return res.json({error: 'Post not exist'});
}
})
.then(function(data) {
post.comments.push(comment._id);
return post.save();
});
.then(function(post) {
return res.json({ok: 1})
})
.catch(function(e)) {
return res.json(error: e);
});
Is this promise written right?
How to write this kind of promise?
Callbacks/Promises is a headache...
You're using bluebird, it supports cancellation. Here's an example:
var Promise = require('bluebird');
// enable cancellation
Promise.config({
cancellation: true
});
// store your promise chain
var myPromise = Promise.resolve().then(() => {
return 'foo';
}).then((res) => {
console.log(res);
// call cancel on the chain when needed
myPromise.cancel();
return res;
}).then((res) => {
// this will not be executed
console.log(res + '2');
});
You just need to throw or return a reject promise to trigger the error handling in promises as show in this example:
Comments.findOne({user: req.user.id}).exce()
.then(function(comment) {
if(comment) {
// to bypass all the other .then() resolve handlers, either
// throw an error here or return a rejected promise
throw new Error(res.json({error: 'Already commented'}));
} else {
return Posts.findOne({postId: req.params.id}).exec();
}
})
Promises are "throw safe" which means that .then() will catch any exception thrown and turn it into a rejected promise. This will bypass any following .then() resolve handlers and will instead go to the next reject handler or .catch() handler.
FYI, you should be careful in your code because when you .catch() and then just return from that, it changes your promise state from rejected to resolved and it will then look like a successful promise when you actually had an error and any caller will think everything was successful. This is because the promise infrastructure assumes that if you .catch() an error and return a value that you have "handled" the error and the state is now resolved successfully. To allow the error to continue to propagate to higher callers from a .catch(), you have to re throw or return a rejected promise:
blah(...).then(...)
.catch(function(e)) {
throw new Error(res.json(error: e));
});

Resources