{"id":13617,"library":"node-cleanup","title":"Node Process Cleanup Handler","description":"The `node-cleanup` package provides a mechanism to install custom cleanup handlers that execute when a Node.js process exits. This includes normal termination (exit code 0), error-induced exits (uncaught exceptions, exit code 1), and receipt of POSIX signals such as SIGINT (Ctrl-C), SIGHUP, SIGQUIT, and SIGTERM. The current stable version is 2.1.2, released recently, following a \"complete rewrite\" in version 2.1.0. Its release cadence appears to be driven by feature enhancements and major architectural changes rather than strict time intervals. Key differentiators include support for multiple independent handlers, asynchronous cleanup for signals (allowing postponement of process termination), delegation of termination decisions to child processes (useful for scenarios with tools like Emacs), and custom `stderr` messages for Ctrl-C and uncaught exceptions. The library focuses on robust and reliable process shutdown management in various scenarios.","status":"active","version":"2.1.2","language":"javascript","source_language":"en","source_url":"https://github.com/jtlapp/node-cleanup","tags":["javascript","node","exit","cleanup","sigint","ctrl-c"],"install":[{"cmd":"npm install node-cleanup","lang":"bash","label":"npm"},{"cmd":"yarn add node-cleanup","lang":"bash","label":"yarn"},{"cmd":"pnpm add node-cleanup","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"This package is primarily designed for CommonJS. Direct ESM `import` might not work without specific Node.js module resolution configurations or transpilation for older versions. For most Node.js projects, use `require`.","wrong":"import nodeCleanup from 'node-cleanup';","symbol":"nodeCleanup","correct":"const nodeCleanup = require('node-cleanup');"},{"note":"`uninstall` is a method directly attached to the `nodeCleanup` function, not a named export. It removes all currently installed cleanup handlers.","wrong":"import { uninstall } from 'node-cleanup';","symbol":"nodeCleanup.uninstall()","correct":"nodeCleanup.uninstall();"}],"quickstart":{"code":"const nodeCleanup = require('node-cleanup');\nlet unsavedData = {\n    data: 'some critical data', // Example data to save\n    save: function(callback) {\n        console.log('Saving unsaved data:', this.data);\n        setTimeout(() => {\n            this.data = ''; // Simulate data being saved\n            callback();\n        }, 100); // Simulate async save operation\n    }\n};\n\nnodeCleanup(function (exitCode, signal) {\n    if (signal) {\n        console.log(`Received signal: ${signal}. Initiating async cleanup.`);\n        unsavedData.save(function done() {\n            // Important: calling process.exit() here would terminate immediately\n            // and not inform the parent process of the signal. Re-emit the signal.\n            process.kill(process.pid, signal);\n        });\n        nodeCleanup.uninstall(); // Prevent handler from being called again on re-emitted signal\n        return false; // Prevent immediate process exit to allow async work\n    }\n    console.log(`Exiting with code: ${exitCode}. Sync cleanup finished.`);\n}, {\n    ctrl_C: \"{^C} caught. Saving data...\", // Custom message for Ctrl+C\n    uncaughtException: \"Uh oh. An uncaught exception occurred:\"\n});\n\nconsole.log(\"Process running. Press Ctrl+C to test async cleanup.\");\n// Simulate some ongoing work to keep the process alive\nsetInterval(() => {\n    // This interval keeps the Node.js process from exiting naturally\n    // until a signal or an uncaught exception occurs.\n}, 1000);","lang":"javascript","description":"This quickstart demonstrates how to install an asynchronous cleanup handler for POSIX signals like SIGINT (Ctrl-C), allowing critical data to be saved before the process fully terminates. It also configures custom `stderr` messages for signals and uncaught exceptions."},"warnings":[{"fix":"To enable default `stderr` messages, either call `nodeCleanup()` with no arguments (`nodeCleanup();`) or explicitly pass an `stderrMessages` object, even if empty, when combining with a handler (e.g., `nodeCleanup(myHandler, {});`). To customize messages, provide them in the `stderrMessages` object (e.g., `nodeCleanup({ ctrl_C: 'My custom Ctrl-C message.' })`).","message":"Starting with `v2.1.0`, default `stderr` messages for Ctrl-C and uncaught exceptions are no longer installed automatically when you only provide a cleanup handler. This changes the default behavior of earlier versions.","severity":"breaking","affected_versions":">=2.1.0"},{"fix":"Ensure your signal cleanup handler explicitly `return false;` if you initiate asynchronous operations. Once cleanup is complete, re-emit the signal using `process.kill(process.pid, signal);` to properly terminate the process and inform the parent, rather than `process.exit()`.","message":"When performing asynchronous cleanup operations in response to a signal, it is critical for the cleanup handler to return `false`. Failure to do so will result in immediate process termination, preventing your async tasks from completing.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Implement logic within the parent's cleanup handler to check for active child processes (e.g., `if (child !== null && signal === 'SIGINT') return false;`) and only allow the parent to exit after the child has handled the signal or exited completely.","message":"Delegating the decision to terminate to a child process (e.g., when a child intercepts SIGINT) requires careful management within the parent's cleanup handler to prevent premature exit.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"search_vec":"'0':31 '1':40 '2.1.0':70 '2.1.2':61 'allow':102 'appear':74 'architectur':83 'asynchron':98 'c':51,128,152 'cadenc':73 'chang':84 'child':112 'cleanup':3,8,16,99,148 'code':30,39 'complet':66 'ctrl':50,127,151 'ctrl-c':49,126,150 'current':57 'custom':15,122 'decis':110 'deleg':107 'differenti':91 'driven':77 'emac':120 'enhanc':80 'error':33 'error-induc':32 'except':37,131 'execut':19 'exit':24,29,35,38,147 'featur':79 'focus':134 'follow':64 'handler':4,17,97 'includ':26,92 'independ':96 'induc':34 'instal':14 'interv':89 'javascript':145 'key':90 'librari':133 'like':119 'major':82 'manag':141 'mechan':12 'messag':124 'multipl':95 'node':1,7,146 'node-cleanup':6 'node.js':22 'normal':27 'packag':9 'posix':44 'postpon':103 'process':2,23,105,113,139 'provid':10 'rather':85 'receipt':42 'recent':63 'releas':62,72 'reliabl':138 'rewrit':67 'robust':136 'scenario':116,144 'shutdown':140 'sighup':52 'sigint':48,149 'signal':45,101 'sigquit':53 'sigterm':55 'stabl':58 'stderr':123 'strict':87 'support':93 'termin':28,106,109 'time':88 'tool':118 'uncaught':36,130 'use':114 'various':143 'version':59,69","created_at":"2026-04-20T01:55:25.697344+00:00","updated_at":"2026-04-20T01:55:25.697344+00:00","problems":[{"fix":"Modify your cleanup handler to include `return false;` when handling a signal and initiating async work. Once the async work is done, explicitly re-emit the signal using `process.kill(process.pid, signal);` to ensure proper termination.","cause":"The cleanup handler did not explicitly return `false` when performing asynchronous operations on signal reception, causing the process to exit before async work could complete.","error":"Process terminated immediately, my async cleanup function didn't finish."},{"fix":"For `node-cleanup`, use `const nodeCleanup = require('node-cleanup');` in CommonJS modules. If you are in an ES Module context, consider using a dynamic import `import('node-cleanup')` or ensure your build system properly handles CommonJS interop.","cause":"Attempting to use `require()` syntax in an ES Module context (`\"type\": \"module\"` in `package.json` or `.mjs` file) or vice-versa (using `import` in a CommonJS context). `node-cleanup` is primarily a CommonJS package.","error":"SyntaxError: Cannot use import statement outside a module or ReferenceError: require is not defined"},{"fix":"Explicitly configure `stderrMessages` when calling `nodeCleanup()`. For example, `nodeCleanup(myHandler, { ctrl_C: 'Custom Ctrl-C message' });` to set custom messages with a handler, or `nodeCleanup({ ctrl_C: 'Custom Ctrl-C message' });` if only messages are needed, or simply `nodeCleanup();` to get the default messages without installing a cleanup handler.","cause":"Since `v2.1.0`, `stderr` messages for `SIGINT` (Ctrl-C) and uncaught exceptions are no longer installed by default and must be explicitly configured.","error":"Ctrl-C or uncaught exception messages are not appearing in stderr."}],"ecosystem":"npm","meta_description":null,"install_score":null,"quickstart_score":null,"quickstart_tag":null,"pypi_latest":null,"cli_name":"","cli_version":null,"type":"library","homepage":null,"github":"https://github.com/jtlapp/node-cleanup","docs":null,"changelog":null,"pypi":null,"npm":"https://www.npmjs.com/package/node-cleanup","openapi_spec":null,"status_page":null,"smithery":null,"categories":["devops"],"base_url":null,"auth_type":null,"provenance":{"verified_status":null,"verified_at":null,"last_verified":"2026-06-17","next_check":"2026-07-18","install_tag":null}}