npm install hint-errorsAdd one line to the top of your entry file. The package catches the first uncaught error, shows a hint, and exits.
require("hint-errors");
const a = 10;
a = 20; // TypeError: Assignment to constant variableWhen the error is fixed, the script continues normally.
For long-running processes, the server entry point formats the failure, flushes the output, and exits with code 1 so an external supervisor can restart the process safely. It does not keep a process alive after an uncaught exception.
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); // shown, then the process exits after flushing
}
res.end("ok");
});
server.listen(3000);Register your own hints for domain-specific errors — custom Error subclasses, internal error codes, anything specific to your codebase — without forking the package:
const { addHint } = require("hint-errors");
addHint({
match: /OrderValidationError/,
hint: "Order failed schema validation — check the payload against orders.schema.json",
});By default, custom hints are checked before every built-in hint, so they
can override a built-in match if needed. Pass { priority: "low" } to only
use your hint as a fallback, checked after all built-in hints:
addHint({ match: "some fallback case", hint: "..." }, { priority: "low" });Works the same way with require("hint-errors/server").
Type declarations are published alongside the package — no @types/
package needed. addHint(), its options, and the underlying hint/error
shapes are fully typed.
Both entry points work with import as well as require:
import "hint-errors";
import { addHint } from "hint-errors";import "hint-errors/server";hint-errors is a local development aid, not a production error handler. It
disables itself automatically when NODE_ENV=production — no listeners
are registered at all, so it has zero effect on how your production process
handles uncaught errors. This protects against the common footgun of
requiring a dev tool in an entry file and forgetting to remove it before
deploying.
If you deliberately want hint-errors active in production, set:
HINT_ERRORS_FORCE=1Node calls every listener registered on uncaughtException and
unhandledRejection, in registration order — it doesn't pick one. If your
error-monitoring tool also registers a handler (and some, like Sentry, call
process.exit() themselves), whichever tool registered first normally wins
the race to run.
hint-errors adds its own handlers with process.prependListener(), so its
formatted message runs before handlers that were already registered when it
loads. It does not remove, save, or manually call other handlers: Node invokes
each one normally and only once, preserving once listeners and passing the
original error or Promise-rejection reason through unchanged.
The formatted message is written synchronously to stdout on this fatal-error
path. That small, potentially blocking write ensures a monitoring handler that
calls process.exit() cannot cut off the hint. It only happens when Node has
already reported an unhandled error; ordinary program output is unaffected.
For Promises, this means hint-errors reacts when Node reports an unhandled
rejection; a rejection that already has a handler is left alone.
hint-errors follows standard CLI color conventions:
| Env var | Effect |
|---|---|
NO_COLOR=1 |
Always disables ANSI color output, regardless of other settings |
FORCE_COLOR=1 |
Forces color on, even when stdout isn't an interactive TTY (useful in CI logs) |
TERM=dumb |
Disables color, matching common terminal-capability checks |
By default, color is only emitted when stdout is an interactive TTY — piping output into a file or a log aggregator (CloudWatch, Datadog, etc.) never receives raw escape codes.
| Error | When it happens |
|---|---|
TypeError |
A value is not of the expected type (e.g. reading a property of undefined) |
ReferenceError |
A variable that doesn't exist or hasn't been initialized is referenced |
SyntaxError |
The code contains invalid JavaScript syntax |
RangeError |
A numeric value is outside the allowed range |
URIError |
A global URI handling function is used incorrectly |
AssertionError |
An assertion from the node:assert module fails |
| Code | When it happens |
|---|---|
ENOENT |
File or directory not found |
EACCES |
Permission denied when accessing a file |
EPERM |
Operation not permitted, common on Windows |
EEXIST |
File already exists |
EISDIR |
Expected a file, got a directory |
ENOTDIR |
Expected a directory, got a file |
ENOTEMPTY |
Directory still has contents |
EMFILE |
Too many files open at once |
MODULE_NOT_FOUND |
Package not installed or path is wrong |
ECONNREFUSED |
Target server isn't running |
ECONNRESET |
Connection dropped by remote host |
ETIMEDOUT |
Server didn't respond in time |
EADDRINUSE |
Port is already taken |
EADDRNOTAVAIL |
Address not available on this machine |
EPIPE |
Writing to an already-closed connection |
EAI_AGAIN |
DNS lookup failed (temporary failure) |
EHOSTUNREACH |
No route to the target host |
- Promise rejected with no
.catch() - Unhandled rejection inside
async/await - Missing
awaitcausing undefined reads
Location detection handles the common CommonJS frame shape
(at fn (file:line:col)), parenless top-level frames common in native ESM
(at file:line:col), and file:// URL frames from native ESM — all
normalized back to a plain filesystem path for display.
Note: bundled or minified production code (webpack, esbuild, Vite) isn't source-mapped yet — reported line numbers for bundled code point at the bundle, not your original source. Source map support is tracked for a future release.
MIT © Louis Miguel