Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to "catch" the throw er; // Unhandled 'error' event in other people's APIs code?

I am using a 3rd party package from npm, which in turn connects to some external API on IP address X.X.X.X and it crashes with the following error. The reason is clear, the network was down for a moment, and BOOM my entire program halts:

events.js:177
      throw er; // Unhandled 'error' event
      ^

Error: connect ENETUNREACH X.X.X.X:80
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1054:14)
Emitted 'error' event at:
    at Socket.socketErrorListener (_http_client.js:410:9)
    at Socket.emit (events.js:200:13)
    at emitErrorNT (internal/streams/destroy.js:91:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
    at processTicksAndRejections (internal/process/task_queues.js:84:9) {
  errno: 'ENETUNREACH',
  code: 'ENETUNREACH',
  syscall: 'connect',
  address: 'X.X.X.X',
  port: 80
}

I am deliberately not saying which module on npm it is that causes the problem because I can't control what 3rd party module authors are doing. If I complained to them they may or may not fix it but my coding life needs to go on. Even if any code I call has a coding error, it shouldn't completely crash my calling script. There should be a way for me to catch the condition before it cramps my style completely.

What I have tried: I use error handling meticulously, try/catch around any 3rd party async library calls etc. Example:

var ThirdPartyModule = require("thirdPartyModule");
try {
   await ThirdPartyModule.doIt("some", "params");
} catch (err) {
   console.log("Ok so the module call failed. Let's try something else here, but don't abort my script please!");
}

The catch does does nothing. Every time the module is called and the connection error happens, it completely tanks my entire program with the above "throw er;".

My question is, what additional "wrapper" code can I write around my calls to the library to "catch" any crashes in code that isn't mine, so my code keeps executing?

I am still kind of a noob in nodejs, so I think I am missing some greater nodejs concept that's at work here. I come from C++ or Java so whenever something crashes in these languages, I can always catch() it so I am puzzled how the module can "escape" try/catch in my main script.

Note that I am not looking for answers like "You should make sure to always be connected to the Internet, that's what ENETUNREACH is about." Instead, I am trying to understand how to be the master of the "sub modules" that I am calling, i.e. catch their errors, or if I can't be their master, I want to understand why what I want is impossible in nodejs.

Thanks!

EDIT: in a similar question linked-to below, a commenter suggests to add a process.on("uncaughtException") handler. I will try this out now.

If this is the correct solution, please post it as an answer and explain the underlying concept. Why can't I just rely on try / catch which is what a reformed Java programmer would do? What did the author of the module on npm do to not pass the Error up the chain in his async function?

EDIT 2: Perhaps my question ends up being a duplicate of Catch all uncaughtException for Node js app - I would love an explanation though why some crash deep down must be caught globally like that, and doesn't percolate up the caller chain. Is it because the 3rd party coder made an error, and failed to transform all possible problems that could occur in his code, into proper throws in his async code?

EDIT 3: Ok I managed to track down the problem. The library that I used was not the culprit. It innocently called and promisified a 4th-party library (also on npm) which connects to a server via http.request(). Just that the author of that 4th-party library forgot to install a request.on('error') callback, so when the Internet connection has problems (never happens, right!), that becomes an unhandled situation. The 3rd party library never gets the expected callback "on error" from the 4th party library, and thus never manages to promisify that error situation. Instead, the error lingers unhandled in nodejs ether, crashing my entire script. So there...

like image 810
blitter Avatar asked Dec 24 '19 02:12

blitter


People also ask

How do you handle uncaughtException?

The correct use of ' uncaughtException ' is to perform synchronous cleanup of allocated resources (e.g. file descriptors, handles, etc) before shutting down the process.

Which event is used for unhandled exceptions in node JS?

The 'uncaughtException' event is emitted when an uncaught JavaScript exception bubbles all the way back to the event loop. By default, Node.


1 Answers

Making absolutely no assumptions about the structure of the code inside the library, you're stuck with the below:

process.on('uncaughtException', function (err) {
    //do something
});

Reason being: If the library was using promises throughout, then the try/catch block would have caught it. It's very likely that inside the library the part throwing the error is inside a callback.

Best practice for async code should be to return an error to a callback, return a rejected Promise or throw inside a async block, and never throw inside functions using callbacks or raw Promises. However it seems extremely likely that is exactly what is happening. There is no way to handle the error gracefully at that point, only the catch-all uncaughtException.

Essentially in the case of a thrown error inside a Promise or a traditional async callback, the code is throwing a synchronous error and there is no synchronous try/catch block to handle it. And you can't inject one from outside the module.

So in short the error has no 'proper' mechanism to be handled. The third party module writers should be ashamed.

like image 168
serakfalcon Avatar answered Nov 15 '22 05:11

serakfalcon