1

So my code is like this:

router.get('/myapi/someotherapi/:id', (request, response) => {
    console.log('api: GET /admin/myapi/someotherapi/:id');
    console.log('Reject star redeem requests by id');
    auth.verifyToken(request, response, { role: 'Admin' }).then(b_tokenValidated => {
        if (b_tokenValidated && b_tokenValidated.statusCode !== 401) {
            const myId = b_tokenValidated.userId;
            const updateLastActive = user.updateLastActive(myId, request);
            const mySpecialFunction = myLib.mySpecialFunction(
                request.params.id, myId
            );
            updateLastActive.then(() => {
                mySpecialFunction.then(r => {
                    generalApiResponseSender(response, r);
                })
                .catch((err) => {
                    console.log('mySpecialFunction failed: ', err);
                    generalApiErrorHandler(response, err);
                })
            })
            .catch((err) => {
                generalApiErrorHandler(response, err);
            })
        }
        else {
            generalApiErrorHandler(
                response,
                {
                    status: 401,
                    message: "You haven't logged in or token has expired."
                }
            )
        }
    }).catch((err) => {
        console.log('verifyToken failed: ', err);
        let errCode = 401;
        let errMsg = "You haven't logged in or token has expired.";
        if (typeof err === 'number') {
            errCode = err;
            errMsg = "Please see error code for more details.";
        }
        generalApiErrorHandler(
            response,
            {
                status: errCode,
                message: errMsg
            }
        )
    })
})

export const generalApiErrorHandler = (response, err) => {
    if (!response.headersSent) {
        if (err['status'] && err['message']) {
            response.status(err['status']).send(err['message']);
        }
        else if (typeof err === 'number') {
            response.sendStatus(err);
        }
        else {
            response.sendStatus(500);
        }
    }
}

export const generalApiResponseSender = (response, data) => {
    if (!response.headersSent) {
        if (typeof data !== "number") {
            response.send(snake2Camel(data));
        }
        else {
            response.sendStatus(data);
        }
    }
}

And I am getting errors like this:

api: GET /myapi/someotherapi/:id
My function is triggered
id:  30
myId:  1
Request not found, abort.
update_query failed, error:  404
mySpecialFunction failed:  404
api: GET /myapi/someotherapi/:id
My function is triggered
id:  30
myId:  1
Request not found, abort.
update_query failed, error:  404
node:internal/process/promises:246
          triggerUncaughtException(err, true /* fromPromise */);
          ^

[UnhandledPromiseRejection: 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(). The promise rejected with the reason "404".] {
  code: 'ERR_UNHANDLED_REJECTION'
}

As you can see, these two call are exactly the same.

The first call triggers the error handler normally and send the error code back to the frontend.

The second call, however, declares that there was no error handler to take care of it, so it chooses to stop the whole application.

Can anyone explain to me why is this happening and how can I prevent it? What can be the reason for the error not being caught by the catch function in the second time?

I guess using return instead of throw could potentially solve the issue, since I define that 404 error. But I don't believe the best practice would be returning the error code just like it is not an error but a valid return value.

PS. I spotted someone said async won't work with catch (UnhandledPromiseRejection despite catching expection), so I removed all async and await from the function. Yet, this still happens.

UPDATE (22/4/2023):

Thanks to @Konrad the application is not stopping itself now. The new log I get is given below:

api: GET /myapi/someotherapi/:id
My function is triggered
id:  30
myId:  1
Nothing is updated, abort.
update_query failed, error:  404
Unhandled Rejection at: Promise Promise { <rejected> 404 } reason: 404
(node:21136) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 5)

Also, the frontend still receives the 404 status as expected, which means the error must have been thrown correctly but just the Node.js failed to find that out.

Terry Windwalker
  • 1,343
  • 2
  • 16
  • 36
  • `async` will work with `catch` the question you've linked didn't have any good answer – Konrad Apr 21 '23 at 11:03
  • Take a look at this: https://stackoverflow.com/questions/43834559/how-to-find-which-promises-are-unhandled-in-node-js-unhandledpromiserejectionwar. Add the promise rejection event handler and we will find out why it fails – Konrad Apr 21 '23 at 11:05
  • @Konrad Thanks for the link! Now the app stops stopping itself and gives me that log. I have updated the question so you can take a look. It starts happening since the second time I call the same API btw. – Terry Windwalker Apr 22 '23 at 03:55
  • Do you have a code somewhere that would do `throw 404` or something similar? – Konrad Apr 22 '23 at 06:54
  • @Konrad `mySpecialFunction` is designed to `throw 404` under the input I give. – Terry Windwalker Apr 22 '23 at 06:55

1 Answers1

0

Your problem is that both updateLastActive and mySpecialFunction are promises that you create at the same time, and both might reject - but you're only handling one of the errors.

You should write either

const myId = b_tokenValidated.userId;
Promise.all([
    user.updateLastActive(myId, request),
    myLib.mySpecialFunction(request.params.id, myId),
]).then(([_, result]) => {
    generalApiResponseSender(response, result);
}, (err) => {
    generalApiErrorHandler(response, err);
});

or

const myId = b_tokenValidated.userId;
user.updateLastActive(myId, request).then(() => {
    myLib.mySpecialFunction(request.params.id, myId).then((result) => {
        generalApiResponseSender(response, result);
    })
    .catch((err) => {
        console.log('mySpecialFunction failed: ', err);
        generalApiErrorHandler(response, err);
    })
})
.catch((err) => {
    generalApiErrorHandler(response, err);
})

or

const myId = b_tokenValidated.userId;
user.updateLastActive(myId, request).then(() => {
    return myLib.mySpecialFunction(request.params.id, myId).catch(err => {
        console.log('mySpecialFunction failed: ', err);
        throw err;
    });
}).then(result => {
    generalApiResponseSender(response, result);
}, err => {
    generalApiErrorHandler(response, err);
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375