javascriptreactjsxmlhttprequestfetchnext.js13

How to get response headers of a failed fetch request in java script


In my react application I'm doing a simple fetch inside my useEffect.

useEffect(() => {
  async function fetchData() {
    const res = await fetch(
      "https://api-production.afghantell.com/apiv1/eligibilities",
    );
    console.log(res.status);
    console.log(res.headers);
  }
  fetchData();
}, []);

Everything is good when the response is successful. But I can't access res when the request fails for example with a 400 Error, Even using try catch blocks wasn't any help. There is a specific X-reason attribute in this failed fetch response that I need to access but I can't.


Solution

  • For comprehensive error handling for fetch you have to consider both a rejected promise, and an unsuccessful network request. MDN wrote it better than I can...

    A fetch() promise only rejects when the request fails, for example, because of a badly-formed request URL or a network error. A fetch() promise does not reject if the server responds with HTTP status codes that indicate errors (404, 504, etc.). Instead, a then() handler must check the Response.ok and/or Response.status properties.

    So keep in mind that if the request was not sent, there will be no Response object to inspect. So for example, if you make a request to a completely fake, made-up URL, the DNS lookup will fail...

    try {
        const response = await fetch('https://some.thing.made.up.com')
    } catch (err) {
        console.log('Here is your error', err);
    }
    

    This produces the output :

    Here is your error TypeError: fetch failed
        at node:internal/deps/undici/undici:12618:11
        at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
        at async file:///Volumes/path/to/sample.js:2:22 {
      cause: Error: getaddrinfo ENOTFOUND some.thing.made.up.com
          at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:118:26) {
        errno: -3008,
        code: 'ENOTFOUND',
        syscall: 'getaddrinfo',
        hostname: 'some.thing.made.up.com'
      }
    }
    

    So for the above case, you cannot inspect the headers, because there are none to speak of. Note the err object in the catch block should be an instance of Error, but it could be several types of error depending on what happened, such as TypeError or AggregateError. From the OP's post, it seems like this is what happened.

    If the requests fail with some status code, eg. 400 or 500 series error, you can inspect the Response object to get more information about what went wrong.

    try {
        const response = await fetch('https://www.google.com/bad/url')
        console.log('my status code...', response.status);
        console.log('my date header...', response.headers.get('date'));
    } catch (err) {
        console.log('Here is your error', err);
    }
    
    

    outputs...

    my status code... 404
    my date header... Mon, 05 May 2025 16:30:40 GMT
    

    Note that I used Node for these examples. In the browser you will get different behavior, but the same concept applies. Unfortunately, this can be confusing, especially in cases of CORS errors, because (at least in Chrome) you will see the request in the Network panel, however, the code will skip right to the catch block, and you will not be able to inspect the response headers.

        useEffect(() => {
            async function fetchData() {
                try {
                    const res = await fetch('https://www.google.com/');
                    console.log('This is never gonna happen', res.status);
                } catch(err) {
                    console.log('Catching TypeError, but I can see CORS error in console', err);
                }
            }
            fetchData();
        }, []);