title: FetchEvent for "[url]" resulted in a network error response: the promise was rejected
body: <p>I am getting those errors and warning in my console after trying to create a PWA - Progressive Web App out of my website using <a href="https://medium.com/@aswin_s/score-a-perfect-100-in-lighthouse-audits-part-1-3199163037" rel="noreferrer">this</a> tutorial. </p>
  
  <blockquote>
    <p>The FetchEvent for
    "<a href="https://www.googletagmanager.com/gtag/js?id=UA-4562443-3" rel="noreferrer">https://www.googletagmanager.com/gtag/js?id=UA-4562443-3</a>" resulted in
    a network error response: the promise was rejected. Promise.then
    (async) (anonymous) @ service-worker.js:228 service-worker.js:1
    Uncaught (in promise) fetch failed 1:21 GET
    <a href="https://www.googletagmanager.com/gtag/js?id=UA-4562443-3" rel="noreferrer">https://www.googletagmanager.com/gtag/js?id=UA-4562443-3</a>
    net::ERR_FAILED The FetchEvent for
    "<a href="https://fonts.googleapis.com/css?family=Open+Sans:300,400&amp;display=swap&amp;subset=cyrillic" rel="noreferrer">https://fonts.googleapis.com/css?family=Open+Sans:300,400&amp;display=swap&amp;subset=cyrillic</a>"
    resulted in a network error response: the promise was rejected.
    Promise.then (async) (anonymous) @ service-worker.js:228
    service-worker.js:1 Uncaught (in promise) fetch failed 1:28 GET
    <a href="https://fonts.googleapis.com/css?family=Open+Sans:300,400&amp;display=swap&amp;subset=cyrillic" rel="noreferrer">https://fonts.googleapis.com/css?family=Open+Sans:300,400&amp;display=swap&amp;subset=cyrillic</a>
    net::ERR_FAILED The FetchEvent for
    "<a href="https://widget.uservoice.com/VuHfPZ0etI2eQ4REt1tiUg.js" rel="noreferrer">https://widget.uservoice.com/VuHfPZ0etI2eQ4REt1tiUg.js</a>" resulted in a
    network error response: the promise was rejected. Promise.then (async)
    (anonymous) @ service-worker.js:228 service-worker.js:1 Uncaught (in
    promise) fetch failed 1:894 GET
    <a href="https://widget.uservoice.com/VuHfPZ0etI2eQ4REt1tiUg.js" rel="noreferrer">https://widget.uservoice.com/VuHfPZ0etI2eQ4REt1tiUg.js</a> net::ERR_FAILED</p>
  </blockquote>
  
  <p>It actually works pretty well. I am able to get a fully working PWA icon in Audits in Chrome Dev Tools. Which is great, but after a refresh I am getting all those errors. My <code>service-worker.js</code> which is located at root of my website looks like this </p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>"use strict";
  
  const SERVICE_WORKER_VERSION = "REPLACED_WITH_SERVICE_WORKER_VERSION"; // updated with tools/service_worker_version.js (String)
  const CACHE_VERSION = SERVICE_WORKER_VERSION;
  //const fileNamesToSaveInCache = ["/"];
  const HOME = "/";
  const OFFLINE_ALTERNATIVE = "/offline";
  const fileNamesToSaveInCache = [];
  const fileNamesToSaveInCacheProd = [
      OFFLINE_ALTERNATIVE,
      "/",
      "/publics/img/favicon/fav.gif",
      "/publics/css/style.css",
      "/publics/css/searchhelp.css",
      "/publics/css/Helpa.css",
  ];
  const rtcLength = 4; // "rtc/".length;
  const rtcFetchDelay = 10000;//ms
  const origin = location.origin;
  const answerFromfileName = {};
  const resolveFromfileName = {};
  const rejectFromfileName = {};
  const timeOutIdFromfileName = {};
  let logLater = [];
  // todo put all into single container
  
  const resolveFetchFromPeerToPeer = function (fileName) {
      clearTimeout(timeOutIdFromfileName[fileName]);
      resolveFromfileName[fileName](answerFromfileName[fileName]);
      delete answerFromfileName[fileName];//stop listening
      delete resolveFromfileName[fileName];
      delete rejectFromfileName[fileName];
  };
  
  const rejectFetchFromPeerToPeer = function (fileName, reason) {
      if (rejectFromfileName[fileName]) {
          rejectFromfileName[fileName](reason);
          delete resolveFromfileName[fileName];
          delete rejectFromfileName[fileName];
      }
  };
  
  const fetchFromPeerToPeer = function (customRequestObject) {
      /*asks all page for a fileName*/
  
      const fileName = customRequestObject.header.fileName;
  
      const promise = new Promise(function (resolve, reject) {
          resolveFromfileName[fileName] = resolve;
          rejectFromfileName[fileName] = reject;
          if (answerFromfileName.hasOwnProperty(fileName)) {
              resolveFetchFromPeerToPeer(fileName);
          }
          timeOutIdFromfileName[fileName] = setTimeout(function() {
              rejectFetchFromPeerToPeer(fileName, "No answer after 10 seconds");
          }, rtcFetchDelay);
      });
  
      self.clients.matchAll().then(function(clientList) {
          clientList.forEach(function(client) {
              client.postMessage(customRequestObject);
          });
      });
      return promise;
  };
  
  const logInTheUI = (function () {
      //console.log("logInTheUI function exists");
      return function (what) {
          console.log(what);
          self.clients.matchAll().then(function(clientList) {
              clientList.forEach(function(client) {
                  client.postMessage({LOG: JSON.parse(JSON.stringify(what))});
              });
          });
      };
  }());
  
  const logInTheUIWhenActivated = function (what) {
      logLater.push(what);
  };
  
  const fetchFromMainServer = function (request, options = {}) {
      /*wrap over fetch. The problem with fetch here, it doesn't reject properly sometimes
      see if statement below*/
      return fetch(request, options).then(function (fetchResponse) {
          // console.log("fetchFromMainServer:", fetchResponse.ok, fetchResponse);
          // logInTheUI([request, options]);
          if ((!fetchResponse) || (!fetchResponse.ok)) {
              return Promise.reject("fetch failed");
          }
          return fetchResponse;
      });
  };
  
  
  const fetchFromCache = function (request) {
      return caches.open(CACHE_VERSION).then(function (cache) {
          return cache.match(request).then(function (CacheResponse) {
              //console.log("fetchFromCache:", CacheResponse.ok, CacheResponse);
              if ((!CacheResponse) || (!CacheResponse.ok)) {
                  return Promise.reject("Not in Cache");
              }
              return CacheResponse;
          });
      });
  };
  
  const isLocalURL = function (url) {
      return !(String(url).match("rtc"));
  };
  
  const fillServiceWorkerCache2 = function () {
      /*It will not cache and also not reject for individual resources that failed to be added in the cache. unlike fillServiceWorkerCache which stops caching as soon as one problem occurs. see http://stackoverflow.com/questions/41388616/what-can-cause-a-promise-rejected-with-invalidstateerror-here*/
      return caches.open(CACHE_VERSION).then(function (cache) {
          return Promise.all(
              fileNamesToSaveInCache.map(function (url) {
                  return cache.add(url).catch(function (reason) {
                      return logInTheUIWhenActivated([url + "failed: " + String(reason)]);
                  });
              })
          );
      });
  };
  
  const latePutToCache = function (request, response) {
      return caches.open(CACHE_VERSION).then(function(cache) {
          cache.put(request, response.clone());
          return response;
      });
  };
  
  const deleteServiceWorkerOldCache = function () {
      return caches.keys().then(function (cacheVersions) {
          return Promise.all(
              cacheVersions.map(function (cacheVersion) {
                  if (CACHE_VERSION === cacheVersion) {
                      //console.log("No change in cache");
                  } else {
                      //console.log("New SERVICE_WORKER_VERSION of cache, delete old");
                      return caches.delete(cacheVersion);
                  }
              })
          );
      });
  };
  
  const useOfflineAlternative = function () {
      return fetchFromCache(new Request(OFFLINE_ALTERNATIVE));
  };
  
  const isAppPage = function (url) {
      /*appPage does not work offline, and we don't serve it if offline
      returns Boolean*/
      return (origin + HOME) === url;
  };
  
  self.addEventListener("install", function (event) {
      /*the install event can occur while another service worker is still active
  
      waitUntil blocks the state (here installing) of the service worker until the
      promise is fulfilled (resolved or rejected). It is useful to make the service worker more readable and more deterministic
  
      save in cache some static fileNames
      this happens before activation */
      event.waitUntil(
          fillServiceWorkerCache2()
          .then(skipWaiting)
      );
  });
  
  self.addEventListener("activate", function (event) {
      /* about to take over, other service worker are killed after activate, syncronous
      a good moment to clear old cache*/
      event.waitUntil(deleteServiceWorkerOldCache().then(function() {
          //console.log("[ServiceWorker] Skip waiting on install caches:", caches);
          return self.clients.claim();
      }));
  });
  
  self.addEventListener("message", function (event) {
      const message = event.data;
      /*
      if (message.hasOwnProperty("FUTURE")) {
          console.log(message.FUTURE);
          return;
      }
      */
      const fileName = message.fileName;
      const answer = message.answer;
      answerFromfileName[fileName] = answer;
      //console.log(fileName, answer, resolveFromfileName);
      if (resolveFromfileName.hasOwnProperty(fileName)) {//
          resolveFetchFromPeerToPeer(fileName);
      }
  });
  
  self.addEventListener("fetch", function (fetchEvent) {
      /* fetchEvent interface FetchEvent
      see https://www.w3.org/TR/service-workers/#fetch-event-interface
      IMPORTANT: fetchEvent.respondWith must be called inside this handler immediately
      synchronously fetchEvent.respondWith must be called with a response object or a
      promise that resolves with a response object. if fetchEvent.respondWith is called
      later in a callback the browser will take over and asks the remote server directly, do not do that
  
      why have fetchEvent.respondWith( and not respond with the return value of the callback function ?
      --&gt;
      It allows to do other thing before killing the service worker, like saving stuff in cache
      */
      const request = fetchEvent.request;//Request implements Body;
      //const requestClone = request.clone(); //no need to clone ?
      const url = request.url;
      if (logLater) {
          logLater.forEach(logInTheUI);
          logLater = undefined;
      }
      // logInTheUI(["fetch service worker " + SERVICE_WORKER_VERSION, fetchEvent]);
      // Needs to activate to handle fetch
      if (isLocalURL(url)) {
          //Normal Fetch
  
          if (request.method === "POST") {
              // logInTheUI(["POST ignored", request]);
              return;
          }
  
          // logInTheUI(["Normal Fetch"]);
          fetchEvent.respondWith(
              fetchFromCache(request.clone()).then(function (cacheResponse) {
                  /* cannot use request again from here, use requestClone */
                  //console.log(request, url);
                  return cacheResponse;
              }).catch(function (reason) {
                  // We don't have it in the cache, fetch it
                  // logInTheUI(fetchEvent);
                  return fetchFromMainServer(request);
              }).then(function (mainServerResponse) {
                  if (isAppPage(url)) {
                      return mainServerResponse;
                  }
                  return latePutToCache(request, mainServerResponse).catch(
                      function (reason) {
                          /*failed to put in cache do not propagate catch, not important enough*/
                          return mainServerResponse;
                      }
                  );
  
              }).catch(function (reason) {
                  if (isAppPage(url)) {
                      //if it is the landing page that is asked
                      return useOfflineAlternative();
                      //todo if we are offline , display /offline directly
                  }
                  return Promise.reject(reason);
              })
          );
      } else {
          // Peer to peer Fetch
          //console.log(SERVICE_WORKER_VERSION, "rtc fetch" url:", fetchEvent.request.url);
          // request, url are defined
          const method = request.method;
          const requestHeaders = request.headers;
  
          //logInTheUI(["Special Fetch"]);
          const customRequestObject = {
              header: {
                  fileName: url.substring(url.indexOf("rtc/") + rtcLength),
                  method
              },
              body: ""
          };
          requestHeaders.forEach(function (value, key) {
              //value, key correct order
              //is there a standard way to use Object.assign with Map like iterables ?
              //todo handle duplicates
              //https://fetch.spec.whatwg.org/#terminology-headers
              customRequestObject.header[key] = value;
          });
  
          //console.log(request);
          fetchEvent.respondWith(
              /*should provide the peer the full request*/
              request.arrayBuffer().then(function (bodyAsArrayBuffer) {
                  const bodyUsed = request.bodyUsed;
                  if (bodyUsed &amp;&amp; bodyAsArrayBuffer) {
                      customRequestObject.body = bodyAsArrayBuffer;
                  }
              }).catch(function (reason) {
                  /*console.log("no body sent, a normal GET or HEAD request has no body",
                  reason);*/
              }).then(function (notUsed) {
                  return fetchFromPeerToPeer(customRequestObject);
              }).then(function (response) {
                  const responseInstance = new Response(response.body, {
                      headers: response.header,
                      status: response.header.status || 200,
                      statusText : response.header.statusText || "OK"
                  });
  
                  return responseInstance;
              }).catch(function (error) {
                  const responseInstance = new Response(`&lt;html&gt;&lt;p&gt;${error}&lt;/p&gt;&lt;/html&gt;`,
                      {
                      headers: {
                          "Content-type": "text/html"
                      },
                      status: 500,
                      statusText : "timedout"
                  });
  
                  return responseInstance;
              })
          );
      }
  
      /*here we could do more with event.waitUntil()*/
  });</code></pre>
  </div>
  </div>
  </p>
  
  <p>I am guessing the problem comes from loading those external libraries. So, this is my code loading those libraries. </p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>// Include the UserVoice JavaScript SDK (only needed once on a page)
       UserVoice = window.UserVoice || [];
       (function() {
           var uv = document.createElement('script');
           uv.type = 'text/javascript';
           uv.async = true;
           uv.src = '//widget.uservoice.com/VuHfPZ0etI2eQ4REt1tiUg.js';
           var s = document.getElementsByTagName('script')[0];
           s.parentNode.insertBefore(uv, s)
       })();
       //
       // UserVoice Javascript SDK developer documentation:
       // https://www.uservoice.com/o/javascript-sdk
       //
       // Set colors
       UserVoice.push(['set', {
               accent_color: '#448dd6',
               trigger_color: 'white',
               trigger_background_color: 'rgba(46, 49, 51, 0.6)'
           }]);
       // Identify the user and pass traits
       // To enable, replace sample data with actual user traits and uncomment the line
       UserVoice.push(['identify', {
               //email:      'john.doe@example.com', // User’s email address
               //name:       'John Doe', // User’s real name
               //created_at: 1364406966, // Unix timestamp for the date the user signed up
               //id:         123, // Optional: Unique id of the user (if set, this should not change)
               //type:       'Owner', // Optional: segment your users by type
               //account: {
               //  id:           123, // Optional: associate multiple users with a single account
               //  name:         'Acme, Co.', // Account name
               //  created_at:   1364406966, // Unix timestamp for the date the account was created
               //  monthly_rate: 9.99, // Decimal; monthly rate of the account
               //  ltv:          1495.00, // Decimal; lifetime value of the account
               //  plan:         'Enhanced' // Plan name for the account
               //}
           }]);
       // Add default trigger to the bottom-right corner of the window:
       UserVoice.push(['addTrigger', {mode: 'contact', trigger_position: 'bottom-right'}]);
       // Or, use your own custom trigger:
       //UserVoice.push(['addTrigger', '#id', { mode: 'contact' }]);
       // Autoprompt for Satisfaction and SmartVote (only displayed under certain conditions)
       UserVoice.push(['autoprompt', {}]);
      });//ready</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,400&amp;display=swap&amp;subset=cyrillic');
  @font-face {
   font-family: 'fa-solid-900';
   font-display: swap;
   src: url(https://use.fontawesome.com/releases/v5.8.2/webfonts/fa-solid-900.woff2) format('woff2');
  }
  @font-face {
   font-family: 'fa-brands-400';
   font-display: swap;
   src: url(https://use.fontawesome.com/releases/v5.8.2/webfonts/fa-brands-400.woff2) format('woff2');
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script async src="https://www.googletagmanager.com/gtag/js?id=UA-number-3"&gt;&lt;/script&gt;
  &lt;script&gt;
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());
  
    gtag('config', 'UA-number-3');
  &lt;/script&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>What should i do in order to fix those errors. This is my first try in PWA so i am lost.</p>