loud-rejection review
loud-rejection 2.2.0 installs process-wide handlers that remember unhandled Promise rejections, print any still unhandled when a Node process exits, and change the exit code to 1. It was built for the era when Node could let an unhandled rejection pass quietly. The README now says the package is moot on Node 15 and newer because Node's default mode throws. Our Node 22 checks could load the CommonJS package through both require() and ESM import, but modern applications already have the failure behavior this module was meant to add.
loud-rejection 2.2.0 installed in 1.2 seconds with 0 audit findings, yet its README says Node 15+ made it moot. Do not add it to a current Node 22 application; keep it only where an older runtime and an existing process-exit contract are both real constraints.
We installed it
| Install | ✓ · 1.2s | 4 packages on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | n/a | could not be bundled for the browser (Node-only code, most likely) |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does loud-rejection install cleanly?
Yes. In a fresh container with an empty cache, npm install loud-rejection finished in 1 seconds, leaving 4 packages and 1 MB on disk. npm audit reported no known vulnerabilities.
Can loud-rejection run in a browser?
Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.
Does loud-rejection work with both ESM and CommonJS?
Yes. Both import 'loud-rejection' and require('loud-rejection') worked in Node 22 in our run. The package is published as CommonJS.
Does loud-rejection include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
loud-rejection or make-promises-safe: which should you use?
make-promises-safe: Use it only for a legacy side-effect import that makes unhandled rejections fatal immediately. loud-rejection 2.2.0 installed in 1.2 seconds with 0 audit findings, yet its README says Node 15+ made it moot.
When should you not use loud-rejection?
Production runs Node 15 or newer. The project's own README says the package is moot because the runtime throws on unhandled rejections by default.
Use it if
- A maintained legacy CLI still runs on Node 8 through 14 and cannot set the runtime's unhandled-rejection mode another way.
- A test suite for an old Node release needs to collect late-handled rejections and fail only when the process exits.
- You are preserving an existing application entry point that already depends on the package's custom logging callback.
- Production runs Node 15 or newer. The project's own README says the package is moot because the runtime throws on unhandled rejections by default.
- You are authoring a reusable module. The README explicitly limits this process-wide listener to top-level tests, CLIs, and applications.
- You need immediate failure at the rejection site. Version 2.2.0 tracks pending rejections and reports what remains at process exit.
- The code runs in a browser. The package depends on Node process signals, and our esbuild browser build failed.
- Long-term maintenance matters. npm version 2.2.0 dates to 2019 and GitHub's last push was January 24, 2021.
Setup reality
We installed loud-rejection 2.2.0 in a fresh Node 22 Bookworm sandbox in 1.2 seconds. It left 4 packages and 1 MB on disk, and npm audit reported 0 known vulnerabilities. The package itself has 2 direct dependencies, 0 peer dependencies, 28 KB unpacked, an MIT license, and bundled TypeScript declarations. It is CommonJS without an exports map; require() and ESM import both worked under Node 22.23.2.
Call loudRejection() once in the application entry point, or load loud-rejection/register for its side effect. No credentials, config files, or native compilation are involved. Registration adds global process listeners, so importing it from a shared library changes the host application's error policy and can collide with the application's own unhandledRejection handling.
Version 2.2.0 waits until process exit, prints each rejection still considered unhandled, and sets exit code 1. A rejection that gains a handler later is removed from its pending set. This timing differs from Node 15+ default throw behavior and from an explicit immediate listener, so tests that assert log order or shutdown behavior can change when the package is removed.
Our browser bundle could not be built with esbuild, which matches the process and signal-exit dependencies. On Node 22, use the runtime default or an explicit top-level policy. If a legacy Node 8 to 14 deployment still requires this shim, pin it, keep registration in one entry file, and test signal-driven shutdown.
Patterns
Enable reporting from an entry file register-handler
const loudRejection = require('loud-rejection');
loudRejection();
startCli();Call version 2.2.0 once at the top level; the package README tells reusable modules to leave the host process untouched.
Load the side-effect entry register-by-import
import 'loud-rejection/register';
await runApplication();The /register file installs listeners as soon as it loads, which makes import order observable in a process with other rejection handlers.
Send rejection stacks to a logger customize-log-output
const loudRejection = require('loud-rejection');
loudRejection((stack) => {
process.stderr.write(`[unhandled rejection]\n${stack}\n`);
});The callback receives the rendered error stack; reporting still occurs for pending rejections when the process exits.
Catch errors inside exported code keep-library-local
async function loadConfig(path) {
try {
return await readConfig(path);
} catch (error) {
error.message = `Cannot load ${path}: ${error.message}`;
throw error;
}
}
module.exports = {loadConfig};A library should return or reject its own Promise; version 2.2.0's global listener belongs only in the consuming application's entry point.
Handle a CLI failure directly catch-cli-main
main().catch((error) => {
console.error(error.stack || error);
process.exitCode = 1;
});On Node 22, an explicit top-level catch gives clearer shutdown control than adding a package whose README says Node 15 made it moot.
Make a legacy runtime throw set-node-policy
node --unhandled-rejections=strict ./cli.jsUse this runtime flag only on Node versions that support it; it changes policy without adding the 4-package install measured for loud-rejection.
Assert failure in a child process test-exit-code
const result = spawnSync(process.execPath, ['fixture.js'], {encoding: 'utf8'});
assert.equal(result.status, 1);
assert.match(result.stderr, /expected rejection/);Version 2.2.0 reports a still-unhandled rejection during shutdown, so test the child process's exit status and STDERR together.
Centralize process listeners avoid-double-registration
if (require.main === module) {
require('loud-rejection')();
main();
}One entry-point guard prevents test imports and library consumers from registering another pair of global listeners.
Confirm Node's current failure mode observe-modern-default
const child = spawnSync(process.execPath, ['-e', "Promise.reject(new Error('boom'))"]);
assert.notEqual(child.status, 0);Node 15 and newer throw unhandled rejections by default, which is the concrete reason the README calls this package moot.
Replace registration with top-level handling remove-legacy-shim
// Remove: require('loud-rejection')();
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});Before removal, compare shutdown timing because version 2.2.0 waits for process exit while a direct catch handles main() immediately.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| make-promises-safe | npm | Use it only for a legacy side-effect import that makes unhandled rejections fatal immediately. |
| p-event | npm | Use it to await a particular EventEmitter event instead of installing a process-wide rejection policy. |
| safe-await | npm | Use it when a project deliberately represents awaited success and failure as returned values. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

