UnhandledPromiseRejectionWarning on handler method abstraction wrapper - node.js

I have a Express router with specific request handler:
router.post(def.doc_create, handle);
async function docCreate(req, res) {
let metadata = mapper.mapObject(req.body, templates.document_create_rq);
let relatedVers = mapper.mapArray('dms$relatedDocuments', templates.related_doc_vers, req.body).items;
// if (req.body['dms$mchMetadata']) {
try {
let productInstanceIds = req.body['dms$mchMetadata']["pro$productInstanceIds"] || [];
} catch (err) {
throw new Error(err.message);
}
....
}
'safeHandleReq' method will pick up the right handler for given route from the pre-initialised map and will call it:
class SafeRequestHandler {
constructor(reqMappings) {
this.mappings = reqMappings;
}
safeHandleReq(req, res) {
try {
let pair = _.find(this.mappings, {'url': req.url});
return pair.handler(req, res);
} catch (err) {
return sendEnhaced(err, res);
}
}
}
with this handler being executed in try-catch I wanted to avoid UnhandledPromiseRejectionWarning: Unhandled promise rejection errors. This happens for example, where there is no such property in req.body that I'm asking for. Now exactly this happens in try-catch block I have shown here. The request's body does not contain dms$mchMetadata property at all. Now I have expected the catch block of the handler to be hit. But it's not. Even when I re-throw caught error in the 'docCreate' handler itself, safeHandleReq's handler catch block won't catch the error, but forementioned error is being written on the console instead.
I need to be able to catch all sorts of this errors in handler's catch block because there is many places where anticipations can go wrong and I need to return (somethong). When the error is not caught execution hangs and Express won't respond.
So why and what I need to do in order to be able to catch all errors from handler implementation in the safeHandleReq try-catch block? I need a systematic solution.
Thanks for recommendations!

Related

rxjs how to handle error in subscriber's next

I'm using rxjs in a library to expose an Observable that clients can subscribe to to consume messages. I want to be able to react appropriately if the subscriber's next function throws an error. However, I'm not seeing any obvious way to detect that. For example:
const observable = new Observable<string>((subscriber) => {
subscriber.next('first')
subscriber.next('second')
subscriber.complete()
})
observable.subscribe(() => {
throw new Error('oh no!')
})
I have tried all of the following, but the errors are bubbled all the way up to a global scope that's surfaced either in an onUnhandledError function provided to the global config, or in absence of that, the node process's unhandledException event.
process.on('uncaughtException', (error) => {
console.error('IN UNCAUGHT EXCEPTION HANDLER', error.message)
})
export function main() {
try {
const observable = new Observable<string>((subscriber) => {
try {
subscriber.next('first')
} catch (error) {
console.error('IN OBSERVABLE CATCH', error.message)
}
subscriber.complete()
}).pipe(
catchError((error) => {
console.error('CATCHERROR PIPE', error.message)
return of('there was an error!!!!')
}),
)
observable.subscribe({
next: (_value) => {
throw new Error('oh no!')
},
error: (error) => {
console.error('IN OBSERVER ERROR HANDLER', error.message)
},
complete: () => console.log('complete!'),
})
} catch (error) {
console.error('IN MAIN CATCH', error.message)
}
}
This logs:
complete!
IN UNCAUGHT EXCEPTION HANDLER oh no!
The docs don't make a big fuss about ensuring that subscribers avoid throwing errors at all costs, but I don't see a standard mechanism for handling it short of some sort of "observer wrapper" (that gets a bit ugly with the overloads).
Turns out that there is effectively no way to handle errors from observers.
When using Observable.subscribe, it wraps your observer functions in a "SafeSubscriber". This then wraps the supplied functions with a ConsumerObserver. This wraps each in a try/catch that, upon errors, either:
sends them to an optional onUnhandledError function you can supply to the rxjs config
sends them "into the ether" to be picked up by the node process
There is no context, and no way to hook into it. Effectively, errors from next, error, or complete handlers just silently disappear.

Throwing exceptions on AWS Lambda with Node12.x in Axios catch handlers

I'm having a hard time trying to understand how I can make this function work. I think it stems from me not really understanding 100% how to handle promises, but I've read all the tutorials I can find and I still don't see how I can do this.
The following is a function, which calls an endpoint on an API. When it's successful, it'll return a token for use in future requests. When it fails, I want to throw an exception and completely stop the execution.
I've heard that when throwing in a catch handler like this, then you're thrown exception is handled by a hidden try/catch and this passes to the next catch handler. But this is effectively meaning my exception gets black-holed. Which I've heard is a thing and I've read other questions on this website about that.
The actual example isn't this trivial. But the effective code is still the same, a try/catch wrapped around all the code in question. But any exceptions never land in the right place. Then lambda fails with the exception in question. But it's not handled by the code that I wrote, so the response is wrong. I need to handle the code and output a different reply based on what exception is being thrown. So I change it around, fix up the data, add other related information and return that. But since I'm never landing in the right place. I can never do that.
How could I alter this function, so that it correctly lands in the exception handler I've given? Thanks for your help
let getToken = async () => {
let transport = axios.create({ baseURL, headers: passwordHeaders });
return transport.post('/authorization/login')
.then(response => {
let accessToken = _.get(response, 'data.access_token', null);
if(accessToken !== null){
let base64 = Buffer.from(accessToken, 'base64');
let token = base64.toString('ascii');
return token.split(";").shift().split(":").pop();
}
throw new LoginError(response.statusText, response.status));
})
.catch(error => {
if(_.get(error, 'response.status', 'null') === 403){
throw new UnauthorizedError(error.response.statusText);
}
throw error;
});
}
try{
let token = await getToken();
}catch(error){
console.log("this exception should land here");
}
As mentioned in the problems the main problem is the mix of then style and async/await. If we lean into the async approach we could do the following:
let getToken = async () => {
let transport = axios.create({ baseURL });
try {
const response = await transport.post("/authorization/login");
let accessToken = _.get(response, "data.access_token", null);
if (accessToken !== null) {
let base64 = Buffer.from(accessToken, "base64");
let token = base64.toString("ascii");
return token.split(";").shift().split(":").pop();
}
} catch (e) {
throw e;
}
};
try {
let token = await getToken();
} catch (error) {
console.log("this exception should land here");
}
As getToken is an asynchronous function it returns a Promise, returning within this function equals to using Promise.resolve and throwing equals to Promise.reject. In my case the catch block of getToken does not do anything and getToken gets called from within a try / catch. We might want to delegate error handling to the outer try catch by removing the inner one.

How to do proper error handling with async/await

I'm writing an API where I'm having a bit of trouble with the error handling. What I'm unsure about is whether the first code snippet is sufficient or if I should mix it with promises as in the second code snippet. Any help would be much appreciated!
try {
var decoded = jwt.verify(req.params.token, config.keys.secret);
var user = await models.user.findById(decoded.userId);
user.active = true;
await user.save();
res.status(201).json({user, 'stuff': decoded.jti});
} catch (error) {
next(error);
}
Second code snippet:
try {
var decoded = jwt.verify(req.params.token, config.keys.secret);
var user = models.user.findById(decoded.userId).then(() => {
}).catch((error) => {
});
user.active = true;
await user.save().then(() => {
}).catch((error) => {
})
res.status(201).json({user, 'stuff': decoded.jti});
} catch (error) {
next(error);
}
The answer is: it depends.
Catch every error
Makes sense if you want to react differently on every error.
e.g.:
try {
let decoded;
try {
decoded = jwt.verify(req.params.token, config.keys.secret);
} catch (error) {
return response
.status(401)
.json({ error: 'Unauthorized..' });
}
...
However, the code can get quite messy, and you'd want to split the error handling a bit differently (e.g.: do the JWT validation on some pre request hook and allow only valid requests to the handlers and/or do the findById and save part in a service, and throw once per operation).
You might want to throw a 404 if no entity was found with the given ID.
Catch all at once
If you want to react in the same way if a) or b) or c) goes wrong, then the first example looks just fine.
a) var decoded = jwt.verify(req.params.token, config.keys.secret);
b) var user = await models.user.findById(decoded.userId);
user.active = true;
c) await user.save();
res.status(201).json({user, 'stuff': decoded.jti});
I read some articles that suggested the need of a try/catch block for each request. Is there any truth to that?
No, that is not required. try/catch with await works conceptually like try/catch works with regular synchronous exceptions. If you just want to handle all errors in one place and want all your code to just abort to one error handler no matter where the error occurs and don't need to catch one specific error so you can do something special for that particular error, then a single try/catch is all you need.
But, if you need to handle one particular error specifically, perhaps even allowing the rest of the code to continue, then you may need a more local error handler which can be either a local try/catch or a .catch() on the local asynchronous operation that returns a promise.
or if I should mix it with promises as in the second code snippet.
The phrasing of this suggests that you may not quite understand what is going on with await because promises are involved in both your code blocks.
In both your code blocks models.user.findById(decoded.userId); returns a promise. You have two ways you can use that promise.
You can use await with it to "pause" the internal execution of the function until that promise resolves or rejects.
You can use .then() or .catch() to see when the promise resolves or rejects.
Both are using the promise returns from your models.user.findById(decoded.userId); function call. So, your phrasing would have been better to say "or if I should use a local .catch() handler on a specific promise rather than catching all the rejections in one place.
Doing this:
// skip second async operation if there's an error in the first one
async function someFunc() {
try {
let a = await someFunc():
let b = await someFunc2(a);
return b + something;
} catch(e) {
return "";
}
}
Is analogous to chaining your promise with one .catch() handler at the end:
// skip second async operation if there's an error in the first one
function someFunc() {
return someFunc().then(someFunc2).catch(e => "");
}
No matter which async function rejects, the same error handler is applied. If the first one rejects, the second one is not executed as flow goes directly to the error handler. This is perfectly fine IF that's how you want the flow to go when there's an error in the first asynchronous operation.
But, suppose you wanted an error in the first function to be turned into a default value so that the second asynchronous operation is always executed. Then, this flow of control would not be able to accomplish that. Instead, you'd have to capture the first error right at the source so you could supply the default value and continue processing with the second asynchronous operation:
// always run second async operation, supply default value if error in the first
async function someFunc() {
let a;
try {
a = await someFunc():
} catch(e) {
a = myDefaultValue;
}
try {
let b = await someFunc2(a);
return b + something;
} catch(e) {
return "";
}
}
Is analogous to chaining your promise with one .catch() handler at the end:
// always run second async operation, supply default value if error in the first
function someFunc() {
return someFunc()
.catch(err => myDefaultValue)
.then(someFunc2)
.catch(e => "");
}
Note: This is an example that never rejects the promise that someFunc() returns, but rather supplies a default value (empty string in this example) rather than reject to show you the different ways of handling errors in this function. That is certainly not required. In many cases, just returning the rejected promise is the right thing and that caller can then decide what to do with the rejection error.

NodeJS: Promise won't catch thrown exceptions

I have a code like this (simplified):
getStreamFor(path) {
// both, remote and local, return a Promise
if(...) { return getRemoteFileStream(path); }
else { return getLocalFileStream(path); }
}
getRemoteFileStream(path) {
// should throw in my case (MyError)
const validPath = validatePath(path);
return readStreamIfValid(validPath);
}
and in the test case:
it('should throw MyError', () => {
return getStreamFor(path)
.then(() => {})
.catch(error => expect(error).to.be.instanceOf(MyError));
});
The problem is, that when the validatePath(path) Method throws (due to invalid path), nothing get caught in the test case promise. The output in my terminal / console is a regular exception as if it was uncaught.
Does anybody have an idea, why the the Promise wouldn't recognize the throw? How can I fix it without probably surrounding the call in the test case with another "try catch" (since the promise should do that for me)?
Maybe there is a general best practise how to structure Promises in order to avoid error swallowings like these?
Thanks for your help!
The problem here is that validatePath() is not part of the promise chain returned by getRemoteFileStream()
One possible solution is the following:
getRemoteFileStream(path) {
return Promise.resolve()
.then(() => validatePath(path))
.then(validPath => readStreamIfValid(validPath));
}
An exception thrown by validatePath() would now be handled in the Promise's catch handler.

Design pattern for use of promises in error conditions [duplicate]

I'm writing a JavaScript function that makes an HTTP request and returns a promise for the result (but this question applies equally for a callback-based implementation).
If I know immediately that the arguments supplied for the function are invalid, should the function throw synchronously, or should it return a rejected promise (or, if you prefer, invoke callback with an Error instance)?
How important is it that an async function should always behave in an async manner, particularly for error conditions? Is it OK to throw if you know that the program is not in a suitable state for the async operation to proceed?
e.g:
function getUserById(userId, cb) {
if (userId !== parseInt(userId)) {
throw new Error('userId is not valid')
}
// make async call
}
// OR...
function getUserById(userId, cb) {
if (userId !== parseInt(userId)) {
return cb(new Error('userId is not valid'))
}
// make async call
}
Ultimately the decision to synchronously throw or not is up to you, and you will likely find people who argue either side. The important thing is to document the behavior and maintain consistency in the behavior.
My opinion on the matter is that your second option - passing the error into the callback - seems more elegant. Otherwise you end up with code that looks like this:
try {
getUserById(7, function (response) {
if (response.isSuccess) {
//Success case
} else {
//Failure case
}
});
} catch (error) {
//Other failure case
}
The control flow here is slightly confusing.
It seems like it would be better to have a single if / else if / else structure in the callback and forgo the surrounding try / catch.
This is largely a matter of opinion. Whatever you do, do it consistently, and document it clearly.
One objective piece of information I can give you is that this was the subject of much discussion in the design of JavaScript's async functions, which as you may know implicitly return promises for their work. You may also know that the part of an async function prior to the first await or return is synchronous; it only becomes asynchronous at the point it awaits or returns.
TC39 decided in the end that even errors thrown in the synchronous part of an async function should reject its promise rather than raising a synchronous error. For example:
async function someAsyncStuff() {
return 21;
}
async function example() {
console.log("synchronous part of function");
throw new Error("failed");
const x = await someAsyncStuff();
return x * 2;
}
try {
console.log("before call");
example().catch(e => { console.log("asynchronous:", e.message); });
console.log("after call");
} catch (e) {
console.log("synchronous:", e.message);
}
There you can see that even though throw new Error("failed") is in the synchronous part of the function, it rejects the promise rather than raising a synchronous error.
That's true even for things that happen before the first statement in the function body, such as determining the default value for a missing function parameter:
async function someAsyncStuff() {
return 21;
}
async function example(p = blah()) {
console.log("synchronous part of function");
throw new Error("failed");
const x = await Promise.resolve(42);
return x;
}
try {
console.log("before call");
example().catch(e => { console.log("asynchronous:", e.message); });
console.log("after call");
} catch (e) {
console.log("synchronous:", e.message);
}
That fails because it tries to call blah, which doesn't exist, when it runs the code to get the default value for the p parameter I didn't supply in the call. As you can see, even that rejects the promise rather than throwing a synchronous error.
TC39 could have gone the other way, and had the synchronous part raise a synchronous error, like this non-async function does:
async function someAsyncStuff() {
return 21;
}
function example() {
console.log("synchronous part of function");
throw new Error("failed");
return someAsyncStuff().then(x => x * 2);
}
try {
console.log("before call");
example().catch(e => { console.log("asynchronous:", e.message); });
console.log("after call");
} catch (e) {
console.log("synchronous:", e.message);
}
But they decided, after discussion, on consistent promise rejection instead.
So that's one concrete piece of information to consider in your decision about how you should handle this in your own non-async functions that do asynchronous work.
How important is it that an async function should always behave in an async manner, particularly for error conditions?
Very important.
Is it OK to throw if you know that the program is not in a suitable state for the async operation to proceed?
Yes, I personally think it is OK when that is a very different error from any asynchronously produced ones, and needs to be handled separately anyway.
If some userids are known to be invalid because they're not numeric, and some are will be rejected on the server (eg because they're already taken) you should consistently make an (async!) callback for both cases. If the async errors would only arise from network problems etc, you might signal them differently.
You always may throw when an "unexpected" error arises. If you demand valid userids, you might throw on invalid ones. If you want to anticipate invalid ones and expect the caller to handle them, you should use a "unified" error route which would be the callback/rejected promise for an async function.
And to repeat #Timothy: You should always document the behavior and maintain consistency in the behavior.
Callback APIs ideally shouldn't throw but they do throw because it's very hard to avoid since you have to have try catch literally everywhere. Remember that throwing error explicitly by throw is not required for a function to throw. Another thing that adds to this is that the user callback can easily throw too, for example calling JSON.parse without try catch.
So this is what the code would look like that behaves according to these ideals:
readFile("file.json", function(err, val) {
if (err) {
console.error("unable to read file");
}
else {
try {
val = JSON.parse(val);
console.log(val.success);
}
catch(e) {
console.error("invalid json in file");
}
}
});
Having to use 2 different error handling mechanisms is really inconvenient, so if you don't want your program to be a fragile house of cards (by not writing any try catch ever) you should use promises which unify all exception handling under a single mechanism:
readFile("file.json").then(JSON.parse).then(function(val) {
console.log(val.success);
})
.catch(SyntaxError, function(e) {
console.error("invalid json in file");
})
.catch(function(e){
console.error("unable to read file")
})
Ideally you would have a multi-layer architecture like controllers, services, etc. If you do validations in services, throw immediately and have a catch block in your controller to catch the error format it and send an appropriate http error code. This way you can centralize all bad request handling logic. If you handle each case youll end up writing more code. But thats just how I would do it. Depends on your use case

Resources