Home » How to Fix Unhandled Promise Rejection Error in React and Async/Await
Web & Code

How to Fix Unhandled Promise Rejection Error in React and Async/Await

✨ Quick Answer

The "Unhandled Promise Rejection" (or Uncaught (in promise)) error occurs in React and JavaScript applications whenever a Promise is rejected—due to a network glitch, bad HTTP response, or runtime exception—without an attached .catch() block or an enclosing try...catch statement. In React apps, unhandled rejections can cause silent UI freezes, stale state, or component crashes in production.

Quick Diagnostics

Cause
Uncaught (in promise) Error in browser developer console during form submit or API fetch: Async function executed without wrapping await calls in a try...catch block
Solution
Wrap await expressions inside try...catch blocks and manage error state
Cause
Unhandled rejection triggered inside a useEffect hook: Promise launched inside an event handler or effect missing proper error handling
Solution
Create an internal async function inside useEffect and handle errors explicitly
Cause
HTTP 4xx/5xx API errors are not caught as promise rejections: The native fetch() API does not reject promises on HTTP error status codes (e.g., 404 or 500)
Solution
Check response.ok before parsing JSON and throw an Error manually if false

Step-by-Step Solution

  1. 1

    Step 1: Wrap async/await Calls in try...catch...finally Blocks

    The primary cause of unhandled rejections is executing asynchronous logic without catching potential failures. Refactor your API handlers:

    JAVASCRIPT
    // ❌ INCORRECT: If network fails or res.json() throws, the promise rejects unhandled
    const handleFetchData = async () => {
      const res = await fetch('/api/user-data');
      const data = await res.json();
      setUser(data);
    };
    
    // ✅ CORRECT: Clean error interception and state management
    const handleFetchData = async () => {
      setLoading(true);
      setError(null);
      try {
        const res = await fetch('/api/user-data');
        if (!res.ok) {
          throw new Error(`HTTP Error: ${res.status} ${res.statusText}`);
        }
        const data = await res.json();
        setUser(data);
      } catch (err) {
        console.error('Failed to fetch user data:', err.message);
        setError(err.message || 'Connection error');
      } finally {
        setLoading(false);
      }
    };
    
  2. 2

    Step 2: Handle Promises Properly Inside useEffect

    In React, callback functions passed to useEffect cannot be directly marked as async because effects must return clean-up functions or undefined. Declare an internal async function:

    JSX
    import React, { useState, useEffect } from 'react';
    
    export const UserProfile = ({ userId }) => {
      const [user, setUser] = useState(null);
      const [error, setError] = useState(null);
    
      useEffect(() => {
        let isMounted = true; // Prevents state updates on unmounted components
    
        const fetchUserProfile = async () => {
          try {
            const response = await fetch(`/api/users/${userId}`);
            if (!response.ok) throw new Error('User profile not found');
            const result = await response.json();
            
            if (isMounted) {
              setUser(result);
            }
          } catch (err) {
            if (isMounted) {
              setError(err.message);
            }
          }
        };
    
        fetchUserProfile();
    
        return () => {
          isMounted = false;
        };
      }, [userId]);
    
      if (error) return <div className="error-banner">Error: {error}</div>;
      if (!user) return <div>Loading...</div>;
    
      return <div>{user.name}</div>;
    };
    
  3. 3

    Step 3: Register a Global Fallback for Unhandled Rejections

    To prevent uncaught promise errors from polluting your application logs unnoticed in production, attach a fallback listener to the window object:

    JAVASCRIPT
    // In index.js / main.jsx / App.jsx
    if (typeof window !== 'undefined') {
      window.addEventListener('unhandledrejection', (event) => {
        console.warn(`[Unhandled Rejection Captured]: ${event.reason}`);
        // Optional: Log error details to Sentry or monitoring service
        // event.preventDefault(); // Suppresses default browser console warning if needed
      });
    }
    

Prevention Advice

Recommended security practices:

  • Remember fetch() Behavior: Native fetch() only rejects on network failures (e.g., DNS lookup failure, connection refused), not on HTTP status 404 or 500. Always check response.ok.
  • Consider Axios for Auto-Rejection: Libraries like axios automatically reject promises for any HTTP response outside the 2xx status range.
  • Implement React Error Boundaries: Pair local async error handling with top-level React Error Boundaries to display graceful fallback UI components when unexpected errors occur.
Author • Web Designer & App Creator

Rodolfo Castro

Web designer, app developer, and founder of SoporteCero. Specializing in UI/UX architecture, digital products, and modern web environments. Every tutorial and guide on SoporteCero is thoroughly tested and verified in our technical lab to ensure reliable, up-to-date solutions.