why-is-node-running
why-is-node-running is a diagnostic utility for Node.js environments, designed to identify active handles (like timers, network connections, and event listeners) that prevent a Node.js process from exiting gracefully. The current stable version is 3.2.2. Releases are active but somewhat irregular, typically driven by Node.js version updates or small feature enhancements and bug fixes. Its key differentiator lies in its ability to pinpoint the exact code locations responsible for these lingering handles, providing stack traces to aid in debugging unexpected process longevity or potential memory leaks, which is crucial for server-side applications and long-running scripts. It supports both programmatic integration and CLI usage, including a convenient `--import` flag for preloading. The package also provides TypeScript type definitions for enhanced developer experience.
Common errors
-
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: ... why-is-node-running/index.js
cause Attempting to `require()` `why-is-node-running` in a CommonJS module after upgrading to v3.0.0 or later.fixUpdate your code to use `import whyIsNodeRunning from 'why-is-node-running';` and ensure your project is configured for ESM (e.g., by adding `"type": "module"` to your `package.json` file or renaming files to `.mjs`). If migration is not feasible, downgrade to `why-is-node-running@2.x`. -
TypeError: whyIsNodeRunning is not a function
cause Incorrect import of the default export, often trying to destructure a named export that doesn't exist, or a mismatch in ESM/CJS expectations.fixEnsure you are using a default import: `import whyIsNodeRunning from 'why-is-node-running';`. If in a CJS context (and using v2.x), it would be `const whyIsNodeRunning = require('why-is-node-running');`.
Warnings
- breaking Version 3.0.0 of `why-is-node-running` introduced significant breaking changes by moving to ECMAScript Modules (ESM) only and raising the minimum required Node.js version to 20.11 or higher. This makes the package incompatible with CommonJS-based environments. Existing projects using `require()` will need to migrate to `import` statements or use `why-is-node-running@v2.x` for CommonJS compatibility.
- gotcha When using `why-is-node-running/include` for preloading, it must be specified via Node.js's `--import` CLI flag, not as a direct `import` statement within your application code. Attempting to `import 'why-is-node-running/include'` might lead to unexpected behavior or errors.
- gotcha The output for file paths in stack traces became relative to the current working directory as of v3.2.0. While not a breaking change in functionality, users accustomed to absolute paths might notice a difference. Full absolute paths are still used if a file is outside the current working directory.
Install
-
npm install why-is-node-running -
yarn add why-is-node-running -
pnpm add why-is-node-running
Imports
- whyIsNodeRunning
const whyIsNodeRunning = require('why-is-node-running');import whyIsNodeRunning from 'why-is-node-running';
- include entrypoint for --import
import 'why-is-node-running/include';
node --import why-is-node-running/include my-app.js
- Types
const whyIsNodeRunning: any = require('why-is-node-running');import whyIsNodeRunning from 'why-is-node-running'; import type { ActiveHandle } from 'why-is-node-running';
Quickstart
import whyIsNodeRunning from 'why-is-node-running'; // Should be the first import for best results
import { createServer } from 'node:net';
console.log('Starting application...');
// Function to simulate a long-running task or server
function startServerAndInterval() {
// A setInterval keeps the process alive
setInterval(() => {
// console.log('Interval running...');
}, 1000);
// A TCP server also keeps the process alive
const server = createServer();
server.listen(0, () => {
const address = server.address();
console.log(`Server listening on port ${typeof address === 'string' ? address : address?.port}`);
});
// Add a listener that might not be cleaned up
process.on('SIGINT', () => {
console.log('Received SIGINT. Shutting down...');
server.close(() => {
console.log('Server closed.');
process.exit(0);
});
});
}
// Call the function multiple times to create several handles
startServerAndInterval();
startServerAndInterval();
// Use setImmediate to log active handles after the current event loop turn
// This will show what's keeping Node.js running.
setImmediate(() => {
console.log('\nChecking for active handles:');
whyIsNodeRunning();
console.log('\nIf the process does not exit, inspect the output above.');
});