Uncatch is a cooperative uncaught exception manager, which allows multiple modules to register listeners for uncaught exceptions, and all of them will be executed before the process finally exits. The module also handles emitting the error and stack trace to STDERR.
As of v1.0.3, Uncatch also treats all unhandled rejections as if they were unhandled exceptions. This is the default behavior in Node.js v15+ anyway, and it allows both of these events to be handled in the same cooperative way in older Node.js versions.
The reason for this module is that Node.js behaves differently when you register an uncaught exception handler. If any listeners are defined for this global event, then the stack trace is no longer emitted to the console, and the process does not exit. It is up to your listener code to handle both operations. This is bad because multiple modules may all want to execute their own emergency shutdown / cleanup code, especially in larger applications, but if any of them were to call process.exit()
(as they should) then not all the listeners get a chance to execute.
Uncatch solves this problem by offering its own global EventEmitter upon which multiple modules may all register listeners. Uncatch then registers a single global uncaught exception handler and fires its own uncaughtException
event, executing all of its listeners before finally emitting the error and stack trace to STDERR, and calling process.exit(1)
.
All modules in your application have to agree to use Uncatch for this to really work. If any module in your stack registers a global uncaught exception handler and calls process.exit()
then all bets are off.
Imagine a modular Node.js daemon application that spawns and manages child processes. If the root daemon suffers an uncaught exception, it should kill off its children as it goes down. This is fine, but what happens when it adds a global uncaught exception handler? That handler needs to call process.exit()
as per best practices, but then no other modules can do anything. For example, the application may have another module that also needs to perform shutdown operations, like logging the exception to a crash log, etc.
If your application can manage this from a single global uncaught exception handler, then you probably don't need Uncatch. But if you have a modular application where multiple separate modules all need to cooperate to run multiple shutdown operations, with a single exit at the very end, then Uncatch may be for you.
What about the uncaught module? This module came close to what I needed, but it doesn't emit the error and stack trace to STDERR, and it doesn't call process.exit()
after all its listeners execute.
What about the uncaught-exception module? It has weird requirements, seems to be built around statsd, its usage is confusing, and the README says things like "v0.10 only". It rubbed me the wrong way.
What about the exception module? This module is rather nifty, but it doesn't solve the problem where multiple modules all need to run uncaught exception code. Also, it has native module requirements (like heapdump).
Download via npm:
npm install uncatch --save
Use it in your modules like this:
const Uncatch = require('uncatch');
Uncatch.on('uncaughtException', function(err) {
// execute your own application shutdown routine here
// do not call any async code, and do not call process.exit
});
//
// ...in some other module...
//
const Uncatch = require('uncatch');
Uncatch.on('uncaughtException', function(err) {
// this code will ALSO run!
});
You do not have to call process.exit()
as Uncatch will do it for you, once all the event listeners are executed. If you need to log the crash, use synchronous filesystem operations like fs.appendFileSync(). Nothing async will execute. It's sync or die during a crash.
By default Uncatch calls process.exit(1)
which signals an unclean exit to whatever calling process or shell spawned your Node.js app. You can change this exit code by setting Uncatch.exitCode
. You can even do this from inside an uncaught exception listener. Example:
Uncatch.on('uncaughtException', function(err) {
// change exit code
Uncatch.exitCode = 65;
});
Please note that the latter prevails here. If multiple modules all fight to customize the exitCode
then whichever one did it last is the winner.
Uncatch recognizes the following environment variables, if they are set to any true value:
Variable | Description |
---|---|
NO_UNCATCH |
If set, do not register an uncaught exception handler. |
NO_UNERROR |
If set, do not emit the error and stack trace to STDERR. |
NO_UNEXIT |
If set, do not call process.exit() . WARNING: Don't do this. Here's why. |
For example, to temporarily disable Uncatch, run your app thusly:
NO_UNCATCH=1 node your-app.js
The MIT License (MIT)
Copyright (c) 2017 - 2024 Joseph Huckaby
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.