Node.js unhandled promise rejection in production
TL;DR
How to find, fix, and prevent unhandled promise rejections that crash Node.js processes in production.
Key facts
- Topic
- Production error triage
- Stack
- Node.js / Linux
TL;DR
An unhandled promise rejection occurs when a Promise rejects and no .catch() handler or try/catch in an async function captures the error. Since Node.js 15, unhandled rejections terminate the process by default, making this a direct production crash vector.
What you see in logs
UnhandledPromiseRejectionWarning: Error: Connection timeout
(Use `node --trace-warnings ...` to show where the warning was created)
In Node.js 15+, the process exits with a non-zero code instead of logging a warning.
Common causes
- Missing
try/catcharoundawaitcalls inasyncfunctions .then()chains without a trailing.catch()- Errors thrown inside a
.then()callback without a downstream catch - Third-party libraries rejecting promises that the caller does not handle
Immediate fix
Add a global safety net (this supplements but does not replace proper error handling):
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled rejection at:', promise, 'reason:', reason);
// Log to Sentry/monitoring, then optionally shut down gracefully
});
Control the behaviour with the --unhandled-rejections flag:
node --unhandled-rejections=warn app.js # Log without crashing
node --unhandled-rejections=throw app.js # Crash (default in Node 15+)
Systematic fix
Audit all async code paths. Every await should be inside try/catch. Every .then() chain should end with .catch():
try {
const data = await fetchData();
} catch (err) {
logger.error('Fetch failed', { error: err.message });
return res.status(500).json({ error: 'Internal error' });
}
Use ESLint rules like no-floating-promises (from @typescript-eslint) to catch missing handlers at build time.
Where Reflex helps
Reflex detects unhandled rejection crashes in your Node.js process logs and correlates them with recent deployments. It can automatically restart the affected process, run health checks to verify recovery, and tag the incident with the triggering deploy SHA for rapid root-cause identification. See How it works.