Unhandled Promise Rejection occurs when a Promise is rejected but there's no .catch() handler or try-catch block to handle the error. This can lead to silent failures and hard-to-debug issues in your application.
// ⌠Problem - No error handling
fetch('/api/users')
.then(res => res.json())
.then(data => console.log(data));
// ✅ Solution 1: Add .catch()
fetch('/api/users')
.then(res => res.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// ✅ Solution 2: Use async/await with try-catch
async function getUsers() {
try {
const res = await fetch('/api/users');
const data = await res.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
}
The most common case - making API calls without handling potential errors.
Using async/await without try-catch blocks leads to unhandled rejections.
Long promise chains need a final .catch() to handle any errors in the chain.
When using Promise.all(), if any promise rejects, the entire operation fails.
// No error handling - will cause unhandled rejection if API fails
fetch('https://api.example.com/users')
.then(response => response.json())
.then(users => {
console.log(users);
displayUsers(users);
});
// If network fails or API returns error, promise is rejected but not caught
// Solution 1: Add .catch() at the end
fetch('https://api.example.com/users')
.then(response => response.json())
.then(users => {
console.log(users);
displayUsers(users);
})
.catch(error => {
console.error('Failed to fetch users:', error);
showErrorMessage('Unable to load users');
});
// Solution 2: Handle HTTP errors properly
fetch('https://api.example.com/users')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(users => {
console.log(users);
displayUsers(users);
})
.catch(error => {
console.error('Error:', error);
showErrorMessage(error.message);
});
async function loadUserData() {
const response = await fetch('/api/user');
const user = await response.json();
console.log(user);
}
loadUserData(); // If fetch fails, unhandled rejection!
// Solution 1: Add try-catch inside function
async function loadUserData() {
try {
const response = await fetch('/api/user');
const user = await response.json();
console.log(user);
} catch (error) {
console.error('Failed to load user:', error);
}
}
loadUserData();
// Solution 2: Catch when calling the function
async function loadUserData() {
const response = await fetch('/api/user');
const user = await response.json();
return user;
}
loadUserData()
.then(user => console.log(user))
.catch(error => console.error('Error:', error));
// Solution 3: Use .catch() on the promise
loadUserData().catch(error => {
console.error('Error:', error);
});
fetch('/api/user')
.then(res => res.json())
.then(user => fetch(`/api/posts/${user.id}`))
.then(res => res.json())
.then(posts => {
console.log(posts);
displayPosts(posts);
});
// Any error in this chain is unhandled
// Add .catch() at the end of the chain
fetch('/api/user')
.then(res => res.json())
.then(user => fetch(`/api/posts/${user.id}`))
.then(res => res.json())
.then(posts => {
console.log(posts);
displayPosts(posts);
})
.catch(error => {
console.error('Error in promise chain:', error);
showErrorMessage('Failed to load data');
});
// Or use async/await for cleaner code
async function loadUserPosts() {
try {
const userRes = await fetch('/api/user');
const user = await userRes.json();
const postsRes = await fetch(`/api/posts/${user.id}`);
const posts = await postsRes.json();
displayPosts(posts);
} catch (error) {
console.error('Error:', error);
showErrorMessage('Failed to load data');
}
}
loadUserPosts();
Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
])
.then(responses => Promise.all(responses.map(r => r.json())))
.then(([users, posts, comments]) => {
console.log(users, posts, comments);
});
// If any fetch fails, unhandled rejection
// Solution 1: Add .catch() to Promise.all()
Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
])
.then(responses => Promise.all(responses.map(r => r.json())))
.then(([users, posts, comments]) => {
console.log(users, posts, comments);
})
.catch(error => {
console.error('Failed to load data:', error);
});
// Solution 2: Use Promise.allSettled() (doesn't reject)
Promise.allSettled([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
])
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Request ${index} succeeded:`, result.value);
} else {
console.error(`Request ${index} failed:`, result.reason);
}
});
});
// Solution 3: Catch individual promises
Promise.all([
fetch('/api/users').catch(err => ({ error: err })),
fetch('/api/posts').catch(err => ({ error: err })),
fetch('/api/comments').catch(err => ({ error: err }))
])
.then(results => {
// Handle results, some may have errors
console.log(results);
});
// Catch all unhandled promise rejections
window.addEventListener('unhandledrejection', event => {
console.error('Unhandled promise rejection:', event.reason);
// Log to error tracking service
// Show user-friendly error message
event.preventDefault(); // Prevent default browser behavior
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
// Log to error tracking service
// Optionally exit process
// process.exit(1);
});
Unhandled promise rejection occurs when a Promise is rejected but there's no .catch() handler or try-catch block to handle the error. This can cause silent failures and make debugging difficult.
Add .catch() handler to promise chains or use try-catch blocks with async/await. Every promise should have error handling either inline or at the end of the chain.
In browsers, it logs a warning to console. In Node.js, it logs a warning and may crash the process in future versions. Always handle rejections to prevent unexpected behavior.
.catch() is used with promise chains (.then()), while try-catch is used with async/await. Both serve the same purpose of handling errors, but try-catch provides cleaner syntax with async/await.
Use Promise.all() when you need all promises to succeed. Use Promise.allSettled() when you want to wait for all promises to complete regardless of success or failure.
Explore 500+ free tutorials across 20+ languages and frameworks.