I am using a thrift client to make a request to a remote server in nodejs. However I am stuck at one point.
This is how I init my client connection & execute Tcommand
try {
const TClient = Thrift.getThriftClient()
const status = await TClient.runCommand(expCommand)
} catch (error) {
throw error // I need exception to be caught here.
}
In the Thrift.getTriftClient() method, I use
getTriftClient () {
const thriftClient = thrift.createClient(command, this.getConnection())
return thriftClient
}
getConnection () {
const connection = thrift.createConnection(this.host, this.port, {
transport: this.transport,
protocol: this.protocol
})
connection.on('error', (err) => {
logger.error(scriptName, 'Thrift connection error', err)
throw new Error('Not able to catch this in try catch')
})
return connection
}
Now my problem is I am unable to re throw any exception which I encounter in error event listener block. What I want is to catch the exception in the first error block instance.
Is there somehow I can do it?
try {
const TClient = Thrift.getThriftClient()
const status = await TClient.runCommand(expCommand)
} catch (error) {
throw error // I need exception to be caught here.
}
The try / catch block way of Error handling is for synchronous code.
The async / await way of Error handling is for asynchronous code.
You have got an await operator, inside of an AsyncFunction instance (async function), therefore you have async code, therefore you are working with Promises, Promises will either resolve to a value or reject with probably an Error instance.
Instead of synchronous:
try {
const TClient = Thrift.getThriftClient()
const status = await TClient.runCommand(expCommand)
} catch (error) {
throw error // I need exception to be caught here.
}
Do asynchronous:
const TClient = Thrift.getThriftClient()
.then((client) => {
return TClient.runCommand(expCommand);
})
.then((commandResult) => {
console.log(commandResult);
})
.catch((err) => {
throw err;
});
Related
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.
I have a function like this:
async getPatient(patientId: string): Promise<PatientDTO> {
const patient = await PatientDAO.getPatients({ id: patientId })
if (patient.length === 0) {
throw new NotFoundError("Patient Not Found!")
}
return patient[0]
}
But I got an error
UnhandledPromiseRejectionWarning: Error: Patient Not Found!
This happened cause I have used async function. How can I make this code running properly?
In order to manage errors in an async function, you have to use a try/catch block:
async getPatient(patientId: string): Promise<PatientDTO> {
try {
const patient = await PatientDAO.getPatients({ id: patientId })
return patient[0]
} catch (error) {
// Do whatever you may want with error
throw error;
}
}
I should mention, that if you simply want to throw the error thats received from getPatients theres no need for a try/catch block at all. Its only needed if you wish to modify the error or perform an extra action according to the error that was thrown.
You have 2 options:
First one is try/catch block with await keyword. Please notice that await has to be used in async function.
try {
const patient = await getPatient(foo);
// handle your data here
} catch(e) {
// error handling here
}
Second one is catch function
getPatient(foo)
.then(patient => {
// handle your data here
}).catch(error => {
// error handling here
});
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.
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;
};
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.