29

I started learning PWA (Progressive Web App) and I have problem, console "throws" error Uncaught (in promise) TypeError: Failed to fetch.

Anyone know what could be the cause?

let CACHE = 'cache';

self.addEventListener('install', function(evt) {
    console.log('The service worker is being installed.');
    evt.waitUntil(precache());
});

self.addEventListener('fetch', function(evt) {
    console.log('The service worker is serving the asset.');
    evt.respondWith(fromCache(evt.request));
});
function precache() {
    return caches.open(CACHE).then(function (cache) {
        return cache.addAll([
            '/media/wysiwyg/homepage/desktop.jpg',
            '/media/wysiwyg/homepage/bottom2_desktop.jpg'
        ]);
    });
}
function fromCache(request) {
    return caches.open(CACHE).then(function (cache) {
        return cache.match(request).then(function (matching) {
            return matching || Promise.reject('no-match');
        });
    });
}
Ku3a
  • 421
  • 1
  • 4
  • 11

6 Answers6

27

I think this is due to the fact that you don't have a fallback strategy. event.respondWith comes with a promise which you have to catch if there's some error.

So, I'd suggest that you change your code from this:

self.addEventListener('fetch', function(evt) {        
    console.log('The service worker is serving the asset.');
    evt.respondWith(fromCache(evt.request));
});                   

To something like this:

addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        if (response) {
          return response;     // if valid response is found in cache return it
        } else {
          return fetch(event.request)     //fetch from internet
            .then(function(res) {
              return caches.open(CACHE_DYNAMIC_NAME)
                .then(function(cache) {
                  cache.put(event.request.url, res.clone());    //save the response for future
                  return res;   // return the fetched data
                })
            })
            .catch(function(err) {       // fallback mechanism
              return caches.open(CACHE_CONTAINING_ERROR_MESSAGES)
                .then(function(cache) {
                  return cache.match('/offline.html');
                });
            });
        }
      })
  );
});          

NOTE: There are many strategies for caching, what I've shown here is offline first approach. For more info this & this is a must read.

BlackBeard
  • 10,246
  • 7
  • 52
  • 62
  • 1
    As of April 2021, Chrome had recently announced this fallbacking would soon be mandatory for PWA installability. Although they have since soften their position, Chrome will still issue various warnings and fetch failures if there is no fallback implemented – defraggled May 02 '21 at 05:05
21

I found a solution to the same error, in my case the error showed when the service worker could not find a file*, fix it by following the network in dev tool of chrome session, and identified the nonexistent file that the service worker did not find and removed array of files to register.

  '/processos/themes/base/js/processos/step/Validation.min.js',
  '/processos/themes/base/js/processos/Acoes.min.js',
  '/processos/themes/base/js/processos/Processos.min.js',
  '/processos/themes/base/js/processos/jBPM.min.js',
  '/processos/themes/base/js/highcharts/highcharts-options-white.js',
  '/processos/themes/base/js/publico/ProcessoJsController.js',
 // '/processos/gzip_457955466/bundles/plugins.jawrjs',
 // '/processos/gzip_N1378055855/bundles/publico.jawrjs',
 // '/processos/gzip_457955466/bundles/plugins.jawrjs',
  '/mobile/js/about.js',
  '/mobile/js/access.js',

*I bolded the solution for me... I start with just a file for cache and then add another... till I get the bad path to one, also define the scope {scope: '/'} or {scope: './'} - edit by lawrghita

lawrghita
  • 29
  • 6
  • 2
    Wow, I worked on this problem for DAYS! It was just as simple as ".png" instead of ".jpg" SMH. Thanks! – Seth B Nov 18 '20 at 18:51
14

I had the same error and in my case Adblock was blocking the fetch to an url which started by 'ad' (e.g. /adsomething.php)

DirtyOne
  • 141
  • 1
  • 4
  • 3
    Hahaha just had the same problem. Used gitea and had a repo starting with "Ad". Almost all pages ended in this exception, deactivating Adblock in my gitea site solved the issue! – Roemer Feb 02 '19 at 20:21
  • This solved my problem after 2 hours. Thank you so much – Luca Iaconelli Aug 31 '20 at 08:33
  • I've also ran into this. I'm using workbox. Any ideas to a work around that does not involve changing directory or forcing end users to turn off ad block? – Adam Youngers May 27 '22 at 18:58
3

In my case, the files to be cached were not found (check the network console), something to do with relative paths, since I am using localhost and the site is inside a sub-directory because I develop multiple projects on a XAMPP server.

So I changed

let cache_name = 'Custom_name_cache';

let cached_assets = [
    '/',
    'index.php',
    'css/main.css',
    'js/main.js'
];

self.addEventListener('install', function (e) {
    e.waitUntil(
        caches.open(cache_name).then(function (cache) {
            return cache.addAll(cached_assets);
        })
    );
});

To below: note the "./" on the cached_assets

let cache_name = 'Custom_name_cache';

let cached_assets = [
    './',
    './index.php',
    './css/main.css',
    './js/main.js'
];

self.addEventListener('install', function (e) {
    e.waitUntil(
        caches.open(cache_name).then(function (cache) {
            return cache.addAll(cached_assets);
        })
    );
});
Marshall Fungai
  • 176
  • 1
  • 6
0

Try to use / before adding or fetching any path like /offline.html or /main.js

Nagibaba
  • 4,218
  • 1
  • 35
  • 43
0

Cached files reference should be correct otherwise the fetch will fail. Even if one reference is incorrect the whole fetch will fail.

let cache_name = 'Custom_name_cache';


let cached_files = [
    '/',
    'index.html',
    'css/main.css',
    'js/main.js'
]; 
// The reference here should be correct. 

self.addEventListener('install', function (e) {
    e.waitUntil(
        caches.open(cache_name).then(function (cache) {
            return cache.addAll(cached_files);
        })
    );
});
Karan Attri
  • 171
  • 2
  • 3