I have an Angular PWA. Its service worker was working flawlessly until I upgraded from Angular 5.0 to 7.2
After the upgrade, I see the following error in /ngsw/state
Driver state: SAFE_MODE (Initialization failed due to error: Invariant violated (initialize): latest hash null has no known manifest initialize/<@https://{{domain}}.com/ngsw-worker.js:2227:27 fulfilled@https://{{domain}}.com/ngsw-worker.js:1772:52 ) Latest manifest hash: none Last update check: never
App background
Code to check for updates
if (this.updates.isEnabled) { const appIsStable$ = this.appRef.isStable.pipe(first(isStable => isStable === true)); const every21600Ms$ = interval(6 * 60 * 60); const every21600MsOnceAppIsStable$ = concat(appIsStable$, every21600Ms$); // poll the service worker to check for updates every21600MsOnceAppIsStable$.subscribe(() => this.updates.checkForUpdate() ); }
My research and troubleshooting steps so far:
In Chrome
In Microsoft edge
My thoughts and expectations
At its simplest, a service worker is a script that runs in the web browser and manages caching for an application. Service workers function as a network proxy. They intercept all outgoing HTTP requests made by the application and can choose how to respond to them.
To bypass the service worker, set ngsw-bypass as a request header, or as a query parameter. The value of the header or query parameter is ignored and can be empty or omitted.
This is the runtime configuration file, that the Angular Service worker will use. This file is built based on the ngsw-config. json file, and contains all the information needed by the Angular Service Worker to know at runtime about which files it needs to cache, and when.
ngsw-cache-bust=0.7064947087867681. It's trying to get the service worker configuration to see what needs updated, but can't access the file. However, this should not impede the service worker from serving up anything it's already cached, and operate normally until back online.
This will be fixed as of Angular version 8.2.14. Below workarounds are only required with older versions.
It seems this is a well known bug in the Angular service worker and that we may expect a patch / working version at some point. Most information can be found in issue 25611 as shobhit vaish already stated.
However, if you are looking for a solution now or you are going to stick with an older Angular 7/8 version longer, which maybe will not get patched soon, I will try to sum up the currently known workarounds. (Also they may not qualify as "proper solution"...)
ngsw-worker.js
You can patch your ngsw-worker.js
file either after your ng run app:build:production
where it will typically reside in the www
folder inside your project (if you haven't changed the Angular outputPath
). Or you can patch it before your build inside node_modules/@angular/service-worker
.
Depending on how often you build your app you could do it simply manually (i.e. with a text editor of your choice) or you write a script for example with the help of sed.
Now you have two options:
handleMessage()
Search for
handleMessage(msg, from) { return __awaiter$5(this, void 0, void 0, function* () {
And replace it with
handleMessage(msg, from) { return __awaiter$5(this, void 0, void 0, function* () { if (this.initialized === null) { this.initialized = this.initialize(); } try { yield this.initialized; } catch (e) { this.state = DriverReadyState.SAFE_MODE; this.stateMessage = `Initialization failed due to handleMessage() error: ${errorToString(e)}`; }
Depending on your @angular/service-worker
version the signature of handleMessage()
may look different for you and you could write a more sophisticated catch
but hopefully / probably it would never reach the catch
block anyways.
This patch basically is what is suggested in a comment by gkalpak and most likely the official fix will also be something like this.
initialize()
Search for
try { // Read them from the DB simultaneously. [manifests, assignments, latest] = yield Promise.all([ table.read('manifests'), table.read('assignments'), table.read('latest'), ]);
And replace it with
try { // Read them from the DB simultaneously. [manifests, assignments, latest] = yield Promise.all([ table.read('manifests'), table.read('assignments'), table.read('latest'), ]); if (latest.latest === null) throw new Error('Error latest.latest is null for what ever reason...');
With this you will ensure that the catch
block of initialize()
will actually run and prevent the service worker from entering SAFE_MODE
. However this may not be as reliable as the first patch but could also work for service workers which are already in SAFE_MODE
due to this problem. It was proposed in a comment by hsta.
If you don't want to mess around with the ngsw-worker.js
you can try to detect the state of the service worker by fetching your-app.domain/ngsw/state
from within your Angular app and if it is in SAFE_MODE
(could be checked for example with a simple regex
search) you can try to reset it by deleting all items from the cache storage and then unregistering the service worker. The idea to this was proposed in a comment by mattlewis92 and approved as probably working hack in a comment by gkalpak.
As shobhit vaish already found out, you can also manually resolve the issue. Next to the possibility which is already stated in the original post you can also do this in the dev tools of chrome based browser under "Application" -> "Clear storage", selecting "Unregister service workers" and hit "Clear site data". Obviously the problem can and probably will come back on future updates and will not help much for normal users. But it may be handy if you as developer just want to resolve it quickly at the moment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With