{"id":28603989,"url":"https://github.com/uber/uncaught-exception","last_synced_at":"2026-03-09T17:12:19.339Z","repository":{"id":12650866,"uuid":"15322507","full_name":"uber/uncaught-exception","owner":"uber","description":"Handle uncaught exceptions.","archived":false,"fork":false,"pushed_at":"2019-07-11T21:10:48.000Z","size":1251,"stargazers_count":66,"open_issues_count":4,"forks_count":8,"subscribers_count":2725,"default_branch":"master","last_synced_at":"2025-05-14T23:43:48.299Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/uber.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-12-19T20:53:16.000Z","updated_at":"2022-07-19T07:20:00.000Z","dependencies_parsed_at":"2022-09-26T18:41:14.335Z","dependency_job_id":null,"html_url":"https://github.com/uber/uncaught-exception","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/uber/uncaught-exception","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uber%2Funcaught-exception","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uber%2Funcaught-exception/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uber%2Funcaught-exception/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uber%2Funcaught-exception/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/uber","download_url":"https://codeload.github.com/uber/uncaught-exception/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/uber%2Funcaught-exception/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259308162,"owners_count":22837974,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-11T17:39:51.744Z","updated_at":"2026-03-09T17:12:19.229Z","avatar_url":"https://github.com/uber.png","language":"JavaScript","readme":"# uncaught-exception\n\nHandle uncaught exceptions.\n\nSupports 0.10 only. Designed for robustness and garaunteed\n    eventual termination of the process.\n\n## Example\n\n```js\nvar uncaughtHandler = require('uncaught-exception');\n\nvar myLogger = {\n    fatal: function fatal(message, metaObj, callback) {\n        // must call the callback once logged\n    }\n}\nvar myStatsd = {\n    immediateIncrement: function inc(key, count, callback) {\n        // call the callback once incremented.\n    }\n}\n\nvar onError = uncaughtHandler({\n    logger: myLogger,\n    statsd: myStatsd,\n    meta: { 'hostname': require('os').hostname() },\n    abortOnUncaught: true, // opt into aborting on uncaught\n    backupFile: '/path/to/uncaught-handler.log',\n    gracefulShutdown: function (callback) {\n        // perform some graceful shutdown here.\n\n        // for example synchronize state of your app to redis\n        // for example communicate to master process in cluster\n        // and ask for a new worker to be started\n\n        // must call callback once gracefully shutdown\n        // after you call the callback the process will shutdown\n    }\n})\n\nprocess.on('uncaughtException', onError)\n```\n\n## Recommended best practices\n\nFor node0.10 and critical services we recommend that you:\n\n - Use a `statsd` and set up alerts \u0026 metrics on \"uncaught exceptions\"\n - Use a `logger` and pay attention to any \"fatal\" logs\n - Use a `backupFile`, this is gold if your logger has an internal\n    uncaught exception or bug.\n - Set `abortOnUncaught` to false. Uncaught exceptions are bad but\n    no availability is worse, where possible you want to avoid the\n    absolute worse case of \"spin crashing\" and continue on exceptions\n    for partial availability, even if that means data corruption.\n\n### Continueing on exceptions is bad\n\nGenerally the nodejs documentation recommends against continuing in\nan undefined state as it can cause cascading failures.\n\nThis is completely correct, however having the system restart itself\nin an automatic fashion causes a different failure mode that's worse.\nGenerally partial availability trumps all.\n\nHowever, uncaught exceptions are serious. You should page the oncall,\neven in the middle of the night. The on call is responsible for taking\nan action whether that's mitigation, failover or restarting the worker.\nHowever the on call must ALWAYS restart the worker.\n\n### Implementing an abort trap\n\nIf you want to partially automate the restarting of workers then I\nwould recommend you implement something like:\n\n - Have your application start in `ABORT` mode, where `abortOnUncaught`\n    is true.\n - Once it aborts, have the parent or the supervisor recognise that\n    it has crashed and will remember to start the application in `NOT_ABORT`\n    mode for the next 24 hours\n\nBy implementing an improved supervisor you can \"throttle\" the amount of\nprocess aborts that happen, this allows a single restart in the common\ncase without manual intervention and maintains partial availability during\na \"spin crash\" or \"high volume uncaught\" scenario.\n\n### How to use the `gracefulShutdown` handler\n\nThe `gracefulShutdown` handler is really a pre-abort asynchronous\nhook used to co-ordinate with your routing infrastructure, for\nexample you may want HAProxy to remove you from the pool or you\nmay want to more gently leave a `ringpop` instance.\n\n## Docs\n\n### Type definitions\n\nSee [docs.mli for type definitions](docs.mli)\n\n### `var onError = uncaughtHandler(options)`\n\n```js\nuncaught-exception/uncaught := (options: {\n    logger: {\n        fatal: (String, Object, Callback) =\u003e void\n    },\n    statsd: {\n        immediateIncrement: (String, Number, Callback) =\u003evoid\n    },\n    meta?: Object,\n    statsdKey?: String,\n    statsdWaitPeriod?: Number,\n    backupFile?: \"stdout\" | \"stderr\" | String,\n    abortOnUncaught?: Boolean,\n    loggerTimeout?: Number,\n    statsdTimeout?: Number,\n    shutdownTimeout?: Number,\n    gracefulShutdown?: (Callback) =\u003e void,\n    preAbort?: () =\u003e void\n}) =\u003e onError: (Error) =\u003e void\n```\n\n`uncaughtHandler` takes an options object and returns an error\n  handling function that can be passed to `'uncaughtException'`\n  listener of the `process`.\n\nYou must pass the `uncaughtHandler` a `logger` with a `fatal()`\n  method.\n\nThe `uncaughtHandler` will exit your process once it's done\n  logging the error.\n\n#### `options.logger`\n\n`options.logger` is a logger object used to log the exception.\n  It's expected to have a `fatal()` method that takes a string,\n  an error object and a callback.\n\nThe `logger` should invoke the `callback` once it's flushed it to\n  all the logging backends you support, (i.e. disk, sentry, etc)\n\n#### `options.statsd`\n\n`options.statsd` is a statsd object used to increment counters.\n  It's expected to have a `immediateIncrement()` method that\n  takes a string, a number and a callback.\n\nThe `statsd` should invoke the `callback` once it's flushed it\n  to the stats service.\n\n#### `options.meta`\n\n`options.meta` allows you to configure the meta object that is\n  logged when an uncaught exception occurs. You might want to \n  put the `os.hostname()` in the meta object.\n\n#### `options.statsdKey`\n\n`options.statsdKey` allows you to configure what kind of statsd\n  key we increment when we have an uncaught exception.\n\nThe key defaults to `\"service-crash\"`.\n\n#### `options.statsdWaitPeriod`\n\n`options.statsdWaitPeriod` is a configurable waiting period.\nThe node implementation of UDP which the `statsd` client will\nprobably uses invokes the callback too early.\n\nIf you `abort()` synchronously there is no garantuee that we've\nactually send the statsd out of the process.\n\nTo work around this we have an \"arbitrary\" waiting period after\nwe get the `statsd` callback.\n\n`options.statsdWaitPeriod` defaults to `1500` milliseconds\n\n#### `options.backupFile`\n\n`options.backupFile` is a filePath that will be appended to\n  synchronously incase anything goes wrong inside the uncaught\n  exception handler.\n\nIt's highly recommended you pass a backup file path in case your\n  logger fails.\n\nInspecting the `backupFile` and looking at the core dump will\n  give you a deep insight into exactly what happened at the\n  end of your node process.\n\nYou may also pass the string literal `\"stdout\"` or `\"stderr\"` as\n  the `options.backupFile` property. If you set it to either\n  `\"stdout\"` or `\"stderr\"` then it will synchronously write to\n  `process.stdout` and `process.stderr` respectively.\n\n**Caveat:** If you are running windows and have set \n  `options.backupFile` to `\"stdout\"` or `\"stderr\"` then it's not\n  garaunteed to be synchronous. In windows any writes to\n  `process.stdout` when `process.stdout` is a `PIPE` will be\n  asynchronous. i.e. `node foo.js | tee file` will involve\n  asynchronous writing to the `backupFile`.\n\n#### `options.abortOnUncaught`\n\nIf `options.abortOnUncaught` is set to `true` the uncaught handler\nwill call graceful shutdown and `process.abort()` for you.\n\nIf this is set to `undefined` or `false` the uncaught handler\nwill not call graceful shutdown and it will not call process abort\n\n#### `options.loggerTimeout`\n\nThe `uncaughtHandler` will assume that your logger might fail or\n  hang so it times out the fatal logging call.\n\nThe default timeout is 30 seconds, you can pass `loggerTimeout`\n  if you want to overwrite it.\n\n#### `options.statsdTimeout`\n\nThe `uncaughtHandler` will assume that your statsd might fail or\n  hang so it times out the statsd increment call.\n\nThe default timeout is 5 seconds, you can pass `statsdTimeout`\n  if you want to overwrite it.\n\n#### `options.gracefulShutdown`\n\nThe `uncaught-exception` module supports doing a graceful\n  shutdown. Normally when an uncaught exception happens you\n  want to close any servers that are open and wait for all\n  sockets to exit cleanly.\n\nThis function only gets called if `abortOnUncaught` is set to\n`true`.\n\nIdeally you want to empty the event loop and do a full graceful\n  shutdown.\n\nYou may also want to communicate to the master process if you are\n  running under `cluster`.\n\nFor more information on proper error handling see the\n  [node domain documentation](http://nodejs.org/api/domain.html#domain_warning_don_t_ignore_errors)\n\n#### `options.shutdownTimeout`\n\nThe `uncaughtHandler` will assume that your gracefulShutdown\n  might fail or hang so it times out the graceful shutdown call.\n\nThe default timeout is 30 seconds, you can pass `shutdownTimeout`\n  if you want to overwrite it.\n\n#### `options.preAbort`\n\nYou can specify your own `preAbort` handler that **MUST** be\n  a synchronous function.\n\nThis function only gets called if `abortOnUncaught` is set to\n`true`.\n\nThe main use case is to invoke your own exit strategy instead of\n  the default exit strategy which is calling `process.abort()`\n\nFor example you may want to `process.exit(1)` here instead.\n\n## Installation\n\n`npm install uncaught-exception`\n\n## tests\n\n`npm test`\n\n## Contributors\n\n - Raynos\n - dfellis\n - squamos\n\n## MIT Licenced\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fuber%2Funcaught-exception","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fuber%2Funcaught-exception","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fuber%2Funcaught-exception/lists"}