Unhandled Promise Rejection
debt(d7/e3/b5/t7)
Closest to 'only careful code review or runtime testing' (d7). ESLint and TypeScript are listed in detection_hints.tools and can flag some patterns (missing await, missing .catch()), but fire-and-forget async calls in loops and Promise.all() misuse often slip past static analysis — the rejection itself may only surface at runtime under specific conditions, and global unhandledrejection handlers are needed to observe them in production.
Closest to 'simple parameterised fix' (e3). The quick_fix describes adding global handlers at entry points and wrapping calls with await/.catch() — a small, repeatable pattern that may touch multiple call sites but doesn't require cross-cutting architectural change. It's more than a single-line patch but well within one component or entry-point scope.
Closest to 'persistent productivity tax' (b5). The issue applies to both web and CLI contexts and recurs across any async code in the codebase. Developers must habitually audit every async call and Promise chain, and the absence of global handlers means monitoring is incomplete — this imposes an ongoing cost across many work streams without being fully architectural.
Closest to 'serious trap' (t7). The misconception field directly states the trap: developers assume a floating rejected Promise is harmless, but in Node.js 15+ it terminates the process and in browsers it silently fires an event that may go unmonitored. This contradicts the intuition that 'fire-and-forget' is safe, and contradicts how synchronous thrown errors are typically visible. The gap between expected (silent ignore) and actual (process crash or unmonitored event) is serious.
Also Known As
TL;DR
Explanation
When a Promise rejects and there is no rejection handler, the error is silently swallowed in older environments. Modern Node.js (15+) terminates the process on unhandled rejection. Browsers fire the window.unhandledrejection event. Common causes: async functions called without await, Promise.all() where one rejection goes unnoticed, and fire-and-forget patterns without .catch(). Fix: always await async calls in try/catch, add .catch() to floating Promises, or use Promise.allSettled() when partial failure is acceptable.
Common Misconception
Why It Matters
Common Mistakes
- Not awaiting async function calls: doSomethingAsync() without await
- Using Promise.all() when Promise.allSettled() would handle partial failure
- Not adding a global unhandledrejection listener for monitoring
Code Examples
fetch('/api/data')
.then(r => r.json())
.then(data => processData(data));
// Rejection not handled — UnhandledPromiseRejection in Node 15+
fetch('/api/data')
.then(r => r.json())
.then(data => processData(data))
.catch(err => console.error('Failed:', err));
// Or with async/await:
try {
const data = await fetch('/api/data').then(r => r.json());
processData(data);
} catch (err) {
console.error('Failed:', err);
}