An unhandled promise rejection occurs when a Promise fails and no .catch() handler or try/catch block exists to handle the error.
What "Unhandled promise rejection" Means
In JavaScript, every Promise must have an error handler. When a Promise rejects and you haven't attached a .catch() method or used await inside a try/catch, the runtime throws this warning. In Node.js, it crashes the process by default (since v15). In browsers, it logs to the console but doesn't break the page.
Why It Happens
The most common causes are straightforward:
- You forgot to attach
.catch()— you called an async function but never handled its rejection path. - You used
awaitoutside atry/catch— the error propagates up to the event loop with nowhere to go. - You assumed a function never rejects — APIs like
fetch()reject on network failure, not just HTTP errors.
Example Code That Triggers It
Here's a minimal Node.js example that produces the exact error:
// app.js — run with: node app.js
async function fetchUserData() {
throw new Error('API key invalid');
}
// The rejection is never handled — this triggers the warning
fetchUserData();
console.log('Still running...');
Run this and you'll see:
node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
UnhandledPromiseRejectionWarning: Error: API key invalid
How to Fix It
The fix is to always attach a rejection handler:
async function fetchUserData() {
throw new Error('API key invalid');
}
// Fix 1: Attach .catch()
fetchUserData().catch((err) => {
console.error('Failed to fetch:', err.message);
});
// Fix 2: Use try/catch with await
(async () => {
try {
await fetchUserData();
} catch (err) {
console.error('Failed to fetch:', err.message);
}
})();
console.log('Still running...');
Both approaches ensure the rejection is consumed. The .catch() approach works for fire-and-forget calls. The try/catch approach is better when you need the result or want to handle errors inline.
Common Mistakes That Cause This
Mistake 1: Handling errors only in the "happy path" callback. Developers often write .then(data => ...) and forget the second argument to .then() or the .catch(). Every .then() chain must end with a rejection handler.
Mistake 2: Conditionally attaching .catch(). Some devs wrap error handling in an if block, thinking errors only happen sometimes. If the Promise rejects when the handler isn't attached, you get the warning. Always attach .catch() unconditionally.
When Should You Worry About This?
You should worry when it appears in production Node.js servers — an unhandled rejection crashes the entire process. In browsers, it's a code-quality issue that hides bugs. In Next.js server components or API routes, it can cause silent failures in server-side rendering. The warning itself is harmless during development, but it's almost always a sign you're missing error handling.
Next time you see "Unhandled promise rejection", check the function that returned the Promise first — did you forget to attach .catch() to it?