TypeError: Failed to fetch a.k.a Pain in the ASS

Ishwar Rimal
4 min readJan 6, 2023

--

Thanks to Mac for making screenshots easy

Ever seen this error in your console?

You might have definitely seen and fixed this:

** usual Story **

The error is consistent -> you go to Google and search for the answer -> Open the first S.O. link -> Without even reading the question, you check the answer -> It can be either of the following:

  1. CORS
  2. Wrong URL
  3. Wrong Protocol
  4. Wrong Method was used.

You realise you missed adding the CORS header on your Back End and BAAM… you’re done.

But life is not that simple my friend.

What if this error is not CONSISTENT?

** A 🦆🤴🏻 NIGHTMARE. **

Is it a CORS? But how come it’s working in most cases?

Is it the wrong URL? No, it can’t be.

Is it the wrong protocol? No man.

Everything is fine with the code, don’t doubt yourself too much. You’re an awesome software engineer.

** A 🦆🤴🏻 Mindfuck **

But why was my error reporting tool reporting this error? Why can’t I reproduce this? Why is life so difficult? Why did I choose software engineering?

The use case was simple, make an API call for certain page events like PageLoad, Button Tap, etc. And if there are any API call failures, throw an error to Airbrake.

fetch('/events/myend/', {
method: 'POST',
headers: {...myBigHead},
body: JSON.stringify(payload)
)
.then(res => console.log(res))
.catch(e => airbrake.throw({msg: `Line 78 : ${e}`)}
})

I tested everything in Dev Environment -> Was working fine.

I tested everything in Staging Env -> Was working fine.

I tested everything in 1 Prod Env -> Was working fine.

** The Give up **

Released to all Prod and in a few days time started seeing this error:

Type Error: Failed to Fetch

Out of 10+ API calls on one user session, 1 or 2 were failing, and it was for less than 1% of the user session. And the weird thing was that it was totally random without any pattern. And yes, it can’t be reproduced.

🦆🤴🏻 hell.

** The HIT **

After debugging for almost a day, and trying random navigation on a page, I noticed something weird in the network tab. One of the XHR requests became red in colour and the status was (cancelled).

** The Actual debugging **

Tried to reproduce the same error by navigating forwards - backwards like a mad fellow 🤷🏻‍♂️ while preserving the network log.

** The Eureka Moment**

Throttled the network to slow 3G and then tried something.

  • Navigated to one page and waited for an XHR call to go to the processing state.
  • Pressed the back button as soon as it was in the processing state.

And the culprit was caught.

** The Logic behind the issue**

So the logic was quite simple. If the browser unloads before the network call is completed, the client closes the request.

Does this mean the network call failed?

I checked the backend logs, and the BE was getting all the payload that I was sending. It’s just that the browser was unable to complete the network call and was throwing the error.

So all these Airbrake alerts were just false alarms.

Why did this happen to me and how can this happen to you too?

Two API calls were causing the issue for me:

  1. One call happened when the page loaded:
    On a slow network a user visits the page -> network call is initiated -> user clicks on the back button.
  2. One call when a button was clicked:
    On a slow network, a user uses the button to open a new tab-> network call is initiated -> user clicks on the back button.

** The Solution **

Attempt1:

I initially thought of using a window variable called isWindowClosed and set it true on onbeforeunload window event.

window.onbeforeunload = (e) => {
window.isWindowClosed = true;
}

And update the network call to be:

fetch('/events/myend/', {
method: 'POST',
headers: {...myBigHead},
body: JSON.stringify(payload)
)
.then(res => console.log(res))
.catch(e => {
!window.isWindowClosed && airbrake.throw({msg: `Line 78 : ${e}`})
}
)

But in some cases the isWindowClosed is still falsy before the airbrake is thrown. My hypothesis for this is that there could be some race condition while setting a value to this variable.

I will do some more debugging and update the doc soon with the final fix. :P

Meanwhile, if you find another solution please let m know in the comments.

I hope you found this article useful. I would love to hear your thoughts. 😇

Thanks for reading. 😊

Cheers! 😃

If you find this article useful, you can show your appreciation by clicking on the clap button. As the saying goes, ‘When we give cheerfully and accept gratefully, everyone is blessed’.

--

--

Ishwar Rimal

Senior FrontEnd Engineer at Intuit. 8 years experience. I write articles on JavaScript, React, Web Optimisation, Startups, Work Life Balance, etc. ❤️ JavaScrip