mrkeyoor.com_
Wed 23 Sept 00:35 UTC
npmUtilsupdated 22 Sept 2026

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.

Verdict

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

Lab card: what happened when we installed loud-rejectionScreenshot of loud-rejection documentation
Install✓ · 1.2s4 packages on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browsern/acould not be bundled for the browser (Node-only code, most likely)
TypesTypeScript types bundled
Known vulns00 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.

API stability4/5Version 2.2.0 has one function, one optional logger, and a register side-effect entry. That interface has not moved since 2019, and our CommonJS and ESM loading checks both succeeded on Node 22.23.2. Stability here comes partly from inactivity: the API solves an old runtime default, and replacing it with current Node behavior can still alter whether a rejection is reported immediately or at process exit.
Docs4/5The README states what the listener records, when it writes to STDERR, the exit code, where registration belongs, and why reusable modules should avoid it. It also gives the decisive current guidance that Node 15 made the package moot. The page does not deeply compare delayed exit reporting with today's throw behavior or explain listener interaction, but it provides enough evidence to reject the package for most new work.
Maintenance1/5npm version 2.2.0 was published in 2019, and GitHub records the last repository push on January 24, 2021. The repository is unarchived and shows 0 open issues and pull requests, but absence of a backlog is weak evidence after more than 5 years without a push. No update is needed to declare modern Node support because the README already directs those users away from the package.
Ecosystem2/5npm counted 4,635,634 downloads in the week ending August 24, 2026, and GitHub shows 281 stars. The large download number likely includes old dependency graphs and build tools; it does not reverse the README's Node 15 warning. Version 2.2.0 works with Node 8 era applications, while browser code and reusable packages are explicitly outside its intended scope.

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.
Skip it if

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.js

Use 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

PackageRegistryPick it when
make-promises-safenpmUse it only for a legacy side-effect import that makes unhandled rejections fatal immediately.
p-eventnpmUse it to await a particular EventEmitter event instead of installing a process-wide rejection policy.
safe-awaitnpmUse 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.