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.
Related
I am struggling with some code... The 2 examples below I would think would work the same but the second example throws an error? I am also struggling to figure out the error, it's not bubbling up? Admittedly I am not a seasoned node developer so any guidance would be much appreciated! If it's relevant the create method in the module is calling the sequelize create.
This works
var p1 = deliverabiltyConfigs.create2(cfgObject);
return Promise.all([p1]).then(function([res1]) {
res.json({result: res1})
});
This does not
deliverabiltyConfigs.create2(cfgObject).then(res1 =>{
res.json({result: res1})
})
Here is the function that I am calling in a controller module
exports.create2 = (dConfig) => {
DeliverabilityConfig.create(dConfig)
.then(data => {
return data
})
.catch(err => {
return {
message:
err.message || "Some error occurred while createing this config."
};
});
};
The create2 function always returns null, so neither invocation will work. Promise.all([p1]) hides the problem, returning a promise to perform an array of no promises.
create2(cfgObject).then(res1 =>{ attempts to invoke then() on null, generating a more obvious error. But neither way works.
Fix by deciding which promise syntax you want, using each as follows:
Using original promise syntax....
exports.create2 = dConfig => {
// note the return
return DeliverabilityConfig.create(dConfig)
.catch(err => {
const message = err.message || "Some error occurred while createing this config.";
return { message };
});
};
// caller
deliverabiltyConfigs.create2(cfgObject).then(result =>{
res.json(result);
})
With recent syntactic sugar...
exports.create2 = async (dConfig) => {
try {
// its fine to not await here, since the caller will await
// but just to illustrate how you might perform more async work here...
return await DeliverabilityConfig.create(dConfig);
} catch (err) {
const message = err.message || "Some error occurred while createing this config."
return { message }
}
}
// caller
var result = await deliverabiltyConfigs.create2(cfgObject);
res.json(result);
Use Promise.all() to run >1 promise concurrently. You've only got one promise in the OP, so no reason for it here.
The code below is an example of what may take place during development.
With the current code, the outer function may throw an error but in this case wont. However, the nested function WILL throw an error (for examples sake). Once it throws the error it cannot be caught as it is asynchronous function.
Bungie.Get('/Platform/Destiny2/Manifest/').then((ResponseText)=>{
//Async function that WILL throw an error
Bungie.Get('/Platform/Destiny2/Mnifest/').then((ResponseText)=>{
console.log('Success')
})
}).catch((error)=>{
//Catch all errors from either the main function or the nested function
doSomethingWithError(error)
});
What I want is for the outer most function to catch all asynchronous function error's but with this code I cannot. I have tried awaiting the nested function but there may be certain circumstances where it will be quicker to not wait for the function. I also tried to include a .catch() with each nested function but this would require a .catch() for each function that would allhandle the error in the same way e.g. doSomethingWithError().
you only needs return the inner function in the outside function.
see example below:
const foo = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo'), 1000);
});
foo.then((res)=>{
console.log(res)
return new Promise((resolve,reject)=>{
setTimeout(() => reject("bar fail"), 1000);
})
}).catch((e)=>{
// your own logic
console.error(e)
});
this is called promise chaining. see this post for more info https://javascript.info/promise-chaining
if you have multiple promises can do something like:
const foo1 = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo1'), 1000);
});
const foo2 = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo2'), 2000);
});
const foo3 = new Promise((resolve,reject) =>{
setTimeout(() => reject('foo3'), 3000);
});
const bar = new Promise((resolve,reject) =>{
setTimeout(() => resolve('bar'), 4000);
});
foo1
.then((res)=>{
console.log(res)
return foo2
})
.then((res)=>{
console.log(res)
return foo3 // throws the error
})
.then((res)=>{
console.log(res)
return bar
})
.catch((e)=>{
// every error will be cached here
console.error(e)
});
I would aim to use async / await unless you have very particular reasons, since it avoids callback hell and makes your code simpler and more bug free.
try {
const response1 = await Bungie.Get('/Platform/Destiny2/Manifest/');
const response2 = await Bungie.Get('/Platform/Destiny2/Mnifest/');
console.log('Success');
} catch (error) {
doSomethingWithError(error);
}
Imagine each Bungie call takes 250 milliseconds. While this is occurring, NodeJS will continue to execute other code via its event loop - eg requests from other clients. Awaiting is not the same as hanging the app.
Similarly, this type of code is used in many browser or mobile apps, and they remain responsive to the end user during I/O. I use the async await programming model in all languages these days (Javascript, Java, C#, Swift etc).
Try this:
let getMultiple = function(callback, ... keys){
let result = [];
let ctr = keys.length;
for(let i=0;i<ctr;i++)
result.push(0);
let ctr2 = 0;
keys.forEach(function(key){
let ctr3=ctr2++;
try{
Bungie.Get(key, function(data){
result[ctr3] = data;
ctr--;
if(ctr==0)
{
callback(result);
}
});
} catch(err) {
result[ctr3]=err.message;
ctr--;
if(ctr==0)
{
callback(result);
}
}
});
};
This should get all your data requests and replace relevant data with error message if it happens.
getMultiple(function(results){
console.log(results);
}, string1, string2, string3);
If the error causes by requesting same thing twice asynchronously, then you can add an asynchronous caching layer before this request.
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 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;
});