Error Handling in Axios vs Fetch API Explained

When making HTTP requests in modern JavaScript applications, handling errors correctly is crucial for application stability and user experience. The fundamental difference between Axios and the native Fetch API regarding response statuses lies in how they treat HTTP error codes (such as 404 or 500). Axios automatically rejects promises when an HTTP status code falls outside the 2xx range, routing them directly to the catch block. In contrast, Fetch resolves the promise normally for any valid HTTP response regardless of the status code, requiring developers to manually verify the status and throw errors themselves.

Error Handling in Fetch API

The native Fetch API only rejects a promise when a network failure occurs, such as a lost internet connection, a DNS lookup failure, or a blocked CORS request. If the server responds with a 404 Not Found, 401 Unauthorized, or 500 Internal Server Error, Fetch considers the request completed successfully and resolves the promise.

To handle HTTP error statuses in Fetch, you must manually inspect the ok property or the status code on the returned Response object.

async function fetchUserData() {
  try {
    const response = await fetch('https://api.example.com/users/999');

    // Fetch does not automatically throw for 4xx or 5xx
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Fetch Error:', error.message);
  }
}

If !response.ok is omitted, the code will proceed to parse the response body (like response.json()), which often leads to unexpected syntax errors if the server returned an HTML error page or an unexpected error payload.

Error Handling in Axios

Axios provides a more automated approach. By default, it rejects the promise for any HTTP response status code that falls outside the 2xx range (status < 200 or status >= 300). This triggers the catch block immediately without requiring manual status validation.

When an HTTP error occurs, Axios creates an error object that contains rich debugging information, accessible via error.response.

import axios from 'axios';

async function fetchUserData() {
  try {
    const response = await axios.get('https://api.example.com/users/999');
    console.log(response.data);
  } catch (error) {
    if (error.response) {
      // The server responded with a status code outside the 2xx range
      console.error('Status Code:', error.response.status);
      console.error('Error Data:', error.response.data);
    } else if (error.request) {
      // The request was made but no response was received (Network error)
      console.error('Network Error:', error.request);
    } else {
      // Something happened in setting up the request
      console.error('Request Setup Error:', error.message);
    }
  }
}

Axios also allows you to customize which HTTP status codes should throw an error using the validateStatus configuration option:

axios.get('/users/999', {
  validateStatus: function (status) {
    return status < 500; // Reject only if status is 500 or higher
  }
});

Summary of Key Differences