Library Affected:
"@vue/cli-plugin-pwa": "^3.6.0",
Browser & Platform:
Google Chrome for Android
Issue or Feature Request Description:
I bundle my website/PWA, where I use a couple of Web Components, using the workbox-webpack-plugin.
Everything works smooth and well till I redeploy the project (hosted on Netlify).
On my phone/desktop, if I clean my cache and access the website/pwa, all good, worker works correctly.
Later on, once I release a new version, where I did several changes, and deploy it to the hosting and if I try to access the website/pwa , then I'll face the following error:
Uncaught SyntaxError: Unexpected token <
On checking network requests, I see that a request is made to an older app.[hash].js file which belonged to previous deploy. It is unable to find this file as it is getting deleted when new sw is activated.
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin')
module.exports = {
configureWebpack: {
plugins: [
new VuetifyLoaderPlugin()
]
},
devServer: {
disableHostCheck: true,
},
lintOnSave: false,
pwa: {
workboxPluginMode: 'InjectManifest',
workboxOptions: {
swSrc: 'src/service-worker.js'
}
}
}
_A similar issue is raised #1528 , #1783_
Please help.
First deploy-

latest deploy- after service worker activation (on reloading the page)

Why is it requesting older files while the hash for app.[hash].js has changed after latest deploy. This is breaking the app.

sw.js file

index.html referencing older app.js file

It sounds like you're running into described in this section of the docs:
Note: If your web app lazy-loads resources that are uniquely versioned with, e.g., hashes in their URLs, it's recommended that you avoid using skip waiting. Enabling it could lead to failures when lazily-loading URLs that were previously precached and were purged during an updated service worker's activation.
Can you make sure that you're not unconditionally calling skipWaiting()? The best way to handle this is to follow a recipe like this, and call skipWaiting() when a user opt-ins, immediately followed by a page refresh once the new service worker activates.
Hey @jeffposnick , thanks for the resources, I've gone through them and looks like "refreshing" is a complicated thing to do.
```import { Events } from './events'
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(${process.env.BASE_URL}service-worker.js, {
ready () {
console.log(
'App is being served from cache by a service worker.\n' +
'For more details, visit https://goo.gl/AFskqB'
)
},
registered () {
console.log('Service worker has been registered.')
},
cached () {
console.log('Content has been cached for offline use.')
},
updatefound () {
console.log('New content is downloading.[update found]')
},
updated (registration) {
console.log('New content is available; please refresh.');
let worker = registration.waiting
Events.$emit("showStatus", {text: "New Content is available. Please Reload the page.",timeout: 0})
worker.postMessage({action: 'skipWaiting'})
},
offline () {
console.log('No internet connection found. App is running in offline mode.')
Events.$emit("showStatus", {text: "You are viewing content offline."})
},
error (error) {
console.error('Error during service worker registration:', error)
}
})
}
We are using vue-cli3-pwa-plugin and this is the boilerplate kind of thing for **registration** code.
As you can see, I'm sending the postMessage after the update( checking if registration.waiting) which then does skipWaiting
self.addEventListener("message", (e)=>{
if (e.data.action=='skipWaiting') {
self.skipWaiting()
}
})
```
This is an approach that is suggested by workbox too. But it just breaks my app when I reload the page after a new service worker is activated, since it tries to find an old cached html file that is not updated well in sw activation phase. The app breaks because it still uses an old html file from the cache that has links to old js files that can't be found anymore.
What am I missing here? Is there a way to use workbox advanced recipe for refreshing using this vue-pwa boilerplate?
In another case, I just deleted the code for skipWaiting and tried to activate new sw after closing all the tabs and opening in new tab. While activation has been successful, after a page reload it's still fetching the old html file and js files that do not exist anymore.
Can you explain me the order of these events for a bug free pwa while using workbox webpack plugin:
This is what I understood about the life cycle, correct me if I'm wrong I would love to understand it better. But I would be glad if you could help me know why my app is using old html file after a page refresh while sw has been activated correctly.
Also, my issue is more related to #1528 and looks like it is solved in workboxV4. But I'm using vue-cli-pwa-plugin which also has workbox-webpack-plugin as dependency. While I'm trying to update the pwa-plugin to latest 3.7.0 which possibly has updated workboxv4, it is still showing me workbox-3.6.3 in package-lock.json.
Any idea how this works?
Thanks
It's possible that your service worker is still waiting to activate after the update is found and even after you refresh. Sometimes refreshing the page won't skip the waiting phase if there are other tasks pending or other clients still open.
You could try using workbox-window to register your service worker, as that library adds some nice logging beyond what register-service-worker has. It'll log a warning to the console if the installing service worker gets stuck in the waiting phase.
Hey @philipwalton && @jeffposnick I have tested it thoroughly and I don't have any issues with service worker activation, It works very fine, I send a refresh prompt while new sw is in waiting phase and on accepting it, it skips waiting and gets activated, I even tested it by closing all tabs and reopening to get it activated and I can see all that happening correctly in dev tools.
There is no issue I see with how my service worker is activating, it works great. but the problem could have been during the cache cleanup phase, something is wrong with it.
Also I see that there is precache-manifest-temp along with pecache-manifest cache in cache API which could be messing up things while I'm still unsure about it.
I've documented the whole flow from first deploy onwards by un-registering existing sw's and cleaning up all caches.










If I add code to delete caches in registration phase like this:
registered (registration) {
console.log('Service worker has been registered.')
caches.keys().then(cacheNames => {
cacheNames.forEach(cacheName => {
caches.delete(cacheName);
});
});
registration.update();
},
only then does it fetch my new html and app.js files otherwise if it's normal like this
registered (registration) {
console.log('Service worker has been registered.')
registration.update();
},
it always tries to fetch old files. But I don't want to delete caches everytime during registration, this just means my caching is useless. Our app is in production already and redeploying has become a nightmare for us. Deleting the caches in registration is the only hack I found so far to not break the app while redeploying. May be you can help me understand why it works fine after I delete previous caches, I have no idea about it. Please help me know what I'm doing wrong here.
I would like you to take a look at #1803 #1528 #1783 I find them very much similar to this but I don't see any solution to them as well. Please help
A few other similar to this:
If you're specifically seeing issues that are tied to the -temp cache and cleanup, then the changes introduced in Workbox v4, detailed in #1793, might be relevant to you.
Migrating to the latest Workbox release is definitely worth trying here.
I am on v4 and facing similar issue (there are no temp caches). Have tried following (as suggested remedies), but to no avail:-
All my hashed cache files use StaleWhileRevalidate strategy. Wondering if the cause of this issue could be a conflict between the strategy (staleWhileRevalidate) I am using and the way Workbox handles cache refresh? I could try NetworkFirst, but that'll go against my app's preferred behaviour. Seconding @sourybunny - deleting old caches on register is also not a solution.
Ensuring smooth redeployments is absolutely critical for a PWA to compete with Native apps. Arguably, native apps have improved a lot when it comes to managing user experience during app updates. I really think there has to be a better, more straightforward way to deal with this issue. The default doesn't seem to be working for me.
@sharanan-sarvsutra
Glad that I found someone facing similar issue, but it's really disappointing that there is no solution to this issue so far. It works with networkFirst but that's not intended for caching resources. Deleting old caches just saved me from the redeploy bug. But I'm still looking for a solution to this. :(
Just clarifying a gap in my understanding of workbox caching - I am using CRA (create react app)'s build tool that auto-hashes the build files. Unlike precached files, it seems files cached via a strategy (such as StaleWhileRevalidate) require manual refresh when modified. Workbox's waiting event callback seems like a good place to do this. Couldn't find a plugin to avoid doing this from scratch.
A few observations:-
1) 'waiting' callback reliably fires each time service worker updates
2) 'skipWaiting' handler is called immediately after
3) 'controlling' handler should be next if service worker updates properly - this is not working in my case, still debugging
Interestingly, I am no longer getting the 'Unexpected token <' error because workbox is able to handle updates to the cached files. Although, workbox is also keeping the old files (which should be removed whenever sw update is available), but it has now stopped referring to old files (the cause of 'Unexpected token <' error).
I simply followed the advanced recipe as prescribed on this page, with the prompt removed. Apparently removing the prompt had an effect on workbox properly loading the updates.
Sharing an update - redeployments are not breaking any service worker logic now. Followed the advanced recipe as suggested in Workbox guides (minus the prompts) + manually handled cache refresh inside workbox's 'waiting' handler.
Hey @sharanan-sarvsutra , can you share a gist/code of how you are doing the manual cache refresh and service worker updating logic.
Thanks.
workbox sw config:-
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
to refresh cache:-
import { Workbox } from 'workbox-window';
export default function workboxRefresh() {
if ('serviceWorker' in navigator) {
const wb = new Workbox('/service-worker.js');
wb.addEventListener('waiting', async (event) => {
await caches.delete('js-cache');
await caches.delete('css-cache');
window.location.reload();
wb.addEventListener('controlling', (event) => {
window.location.reload();
});
wb.messageSW({ type: 'SKIP_WAITING' });
});
wb.register();
}
}
@jeffposnick @sharanan-sarvsutra
I managed to make it work, currently with the following process (first step two steps needed for iOS)
window.location.reload() if server version changesupdated hook, I registration.waiting.postMessage({ type: 'SKIP_WAITING'}) and window.location.reload() againBut I wonder, Is there another way than reloading again after the SW updated in order to prevent the error occurring when precached resources mismatch due to hashes in their url.
window.location.reload() after the SW update actually do in this case, does it update those precached urls?ignoreURLParametersMatching: [/./] instead in order to prevent the very same error without doing either of the two measures above?Thanks for some clarification.
Most helpful comment
Hey @jeffposnick , thanks for the resources, I've gone through them and looks like "refreshing" is a complicated thing to do.
```import { Events } from './events'
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(
${process.env.BASE_URL}service-worker.js, {ready () {
console.log(
'App is being served from cache by a service worker.\n' +
'For more details, visit https://goo.gl/AFskqB'
)
},
registered () {
console.log('Service worker has been registered.')
},
cached () {
console.log('Content has been cached for offline use.')
},
updatefound () {
console.log('New content is downloading.[update found]')
},
updated (registration) {
console.log('New content is available; please refresh.');
let worker = registration.waiting
Events.$emit("showStatus", {text: "New Content is available. Please Reload the page.",timeout: 0})
})
}
self.addEventListener("message", (e)=>{
if (e.data.action=='skipWaiting') {
self.skipWaiting()
}
})
```
This is an approach that is suggested by workbox too. But it just breaks my app when I reload the page after a new service worker is activated, since it tries to find an old cached html file that is not updated well in sw activation phase. The app breaks because it still uses an old html file from the cache that has links to old js files that can't be found anymore.
What am I missing here? Is there a way to use workbox advanced recipe for refreshing using this vue-pwa boilerplate?
In another case, I just deleted the code for skipWaiting and tried to activate new sw after closing all the tabs and opening in new tab. While activation has been successful, after a page reload it's still fetching the old html file and js files that do not exist anymore.
Can you explain me the order of these events for a bug free pwa while using workbox webpack plugin:
1.Registration and Installation happen first.
my old html and js and replace with newer ones.
using old version
activated.
This is what I understood about the life cycle, correct me if I'm wrong I would love to understand it better. But I would be glad if you could help me know why my app is using old html file after a page refresh while sw has been activated correctly.
Also, my issue is more related to #1528 and looks like it is solved in workboxV4. But I'm using vue-cli-pwa-plugin which also has workbox-webpack-plugin as dependency. While I'm trying to update the pwa-plugin to latest 3.7.0 which possibly has updated workboxv4, it is still showing me workbox-3.6.3 in package-lock.json.
Any idea how this works?
Thanks