Three steps. Which entry point you require depends on whether the process entry point should be used, with safe termination after an uncaught error.
npm install hint-errors
require("hint-errors");
// the rest of your entry file, unchanged
Run your script. The first uncaught exception or unhandled
rejection now prints a hint block instead of a raw trace, and
process.exitCode is set to 1.
npm install hint-errors
require("hint-errors/server");
// the rest of your entry file, unchanged
Prints a hint on every uncaught error, same as the default entry point. The process stays running afterward instead of exiting, so one bad request doesn't take down the whole server.
hint-errors is a development aid. It disables
itself automatically when NODE_ENV=production. See
Production Safety for what that means
and how to override it.
Registering a listener on Node's uncaughtException or
unhandledRejection event hands control to
hint-errors. Node stops printing its own trace and deciding
whether to exit, and that responsibility moves to the listener. A
four-stage pipeline runs on every error, in this order.
Two entry points cover two different runtime shapes:
process.exitCode = 1, then lets the process end
on its own.
The same pipeline, wired to whichever kind of process you're running.
Scripts & CLIs
The default entry point. Best for one-off scripts, build tools, and CLIs where exiting on failure is the correct behavior.
require("hint-errors");
run();
On an uncaught error, it prints the hint block, sets
process.exitCode = 1, and lets the event loop drain.
There's no forced process.exit(), so buffered stdout
isn't dropped when the output is piped.
Servers
Use the server entry point when a long-running process needs the same formatted diagnostics while still terminating safely after an uncaught failure.
require("hint-errors/server");
const http = require("http");
const server = http.createServer((req, res) => {
if (req.url === "/crash") {
const user = undefined;
console.log(user.name); // hint prints, then exits after flushing
}
res.end("ok");
});
server.listen(3000);
The formatted diagnostic is flushed before the process exits, so a process supervisor can restart the server with the complete hint instead of leaving a potentially corrupted process alive.
ESM
Thin .mjs shims (index.mjs,
server.mjs) re-export the same CommonJS
implementation through package.json's conditional
exports map. Node evaluates the same underlying
module either way, so there's no separate logic to drift out of
sync.
import "hint-errors";
import { addHint } from "hint-errors";
Same for hint-errors/server:
import "hint-errors/server" works directly, no
createRequire() workaround needed in a
"type": "module" project.
Before v1.3.0, a team's own domain-specific errors (a custom
OrderValidationError, an internal error code) had no
supported way to get a hint without forking the package.
addHint() is exported from both entry points and
registers a { match, hint } entry at runtime, in
exactly the shape the built-in list already uses.
const { addHint } = require("hint-errors");
class OrderValidationError extends Error {
constructor(message) {
super(message);
this.name = "OrderValidationError";
}
}
addHint({
match: "OrderValidationError",
hint: `An order failed validation before it reached payment.
Check the cart contents against current stock and pricing rules
before retrying checkout.`,
});
require("hint-errors");
Match order
Matching stops at the first hit, so by default a custom hint is
checked before every built-in entry. Your own
domain errors win over a generic pattern that might otherwise
match first. Pass { priority: "low" } to invert that:
the custom hint is only tried after every built-in has failed to
match, which is useful for a catch-all fallback that shouldn't
shadow anything more specific.
addHint(
{ match: /timeout/i, hint: "Check the upstream service's health dashboard." },
{ priority: "low" }
);
Passing an invalid entry (missing match or
hint, the wrong type, or an unrecognized
priority) throws a TypeError immediately
instead of failing silently later.
Everything hint-errors exposes. If it isn't listed here, it isn't part of the public API.
Entry points
process.exitCode after an unhandled
error. Exports { addHint }.
{ addHint }.
Functions
match is a string or a RegExp tested
against "ErrorType: message";
hint is the string shown when it matches.
options.priority defaults to
"high" (checked before every built-in); set to
"low" to check it only after every built-in has
been tried. Throws a TypeError for a missing or
malformed entry.
Environment variables
"production", hint-errors registers no
listeners at all and has zero effect on the process.
NODE_ENV=production.
FORCE_COLOR.
"dumb", color output is disabled,
matching how most terminal tooling treats that value.
Every pattern below is a real entry from
src/hints.js. The full list covers 40+ cases across
JavaScript errors, Node/OS errors, and network errors. Anything
that doesn't match still gets a generic, actionable fallback
instead of nothing.
No matching entries. Try a different search term.
Try it
hint-errors is built to disappear the moment it isn't wanted.
NODE_ENV on load. When
it's "production", no listeners are registered,
so the package has zero effect on the process. A
A colored notice explains why. Set
HINT_ERRORS_FORCE=1 if you genuinely want it
active anyway.
uncaughtException/unhandledRejection
listeners already registered, removes them, installs its own
listener first, then re-invokes the originals with the
original error. Nothing is dropped or replaced. Only the order
changes, so Sentry, Winston, or an APM agent still runs, just
after the hint prints.
What's intentionally not solved yet, stated plainly.
-
Source-map resolution Bundled or minified production code (webpack, esbuild, Vite output) will report the location in the built file, not the original source. This is a documented limitation rather than a line number that's silently wrong.
-
No published web playground The "try it" widget on this page runs against a small mirrored dataset in the browser, not a live Node process. A real hosted playground is planned for a future phase.
-
Manual steps Pushing a version tag with
git push --follow-tagscreates the GitHub Release automatically. Only publishing to npm is a manual step — runnpm publishafter the tag is up.