workbox: Improve logging when Chrome's offline check fails
Welcome! Please use this template for reporting bugs or requesting features. For questions about using Workbox, the best place to ask is Stack Overflow, tagged with [workbox]
: https://stackoverflow.com/questions/ask?tags=workbox
Library Affected: workbox workbox-sw, workbox-build, etc.
Browser & Platform: Google Chrome 89 E.g. Google Chrome v51.0.1 for Android, or “all browsers”.
Issue or Feature Request Description: Uncaught (in promise) TypeError: Failed to fetch
My start_url
is set to /?utm_source=web_app_manifest&utm_medium=web_app_manifest&utm_campaign=web_app_manifest
When reporting bugs, please include relevant JavaScript Console logs and links to public URLs at which the issue could be reproduced.
It also errors when deployed on a https url
My config looks like this:
import { skipWaiting, clientsClaim } from "workbox-core"
import {
cleanupOutdatedCaches,
precacheAndRoute,
matchPrecache,
} from "workbox-precaching"
import { setCatchHandler } from "workbox-routing"
import { registerRoute } from "workbox-routing"
import {
NetworkFirst,
StaleWhileRevalidate,
CacheFirst,
} from "workbox-strategies"
import { CacheableResponsePlugin } from "workbox-cacheable-response"
import { ExpirationPlugin } from "workbox-expiration"
import * as googleAnalytics from "workbox-google-analytics"
googleAnalytics.initialize()
cleanupOutdatedCaches()
precacheAndRoute(self.__WB_MANIFEST)
clientsClaim()
function cacheKeyWillBeUsed({ request }) {
const url = new URL(request.url)
url.pathname = url.pathname.replace(/\/index\.html$/, "/")
url.pathname = url.pathname.replace(/\.html$/, "/")
return url.href
}
const networkFirstStrategy = new NetworkFirst({
cacheName: "docs-cache",
fetchOptions: {
credentials: "include",
},
plugins: [{ cacheKeyWillBeUsed }],
})
// Cache page navigations (html) with a Network First strategy
registerRoute(
// Check to see if the request is a navigation to a new page
({ request }) => request.mode === 'navigate',
// Use a Network First caching strategy
new NetworkFirst({
// Put all cached files in a cache named 'pages'
cacheName: 'pages',
plugins: [
// Ensure that only requests that result in a 200 status are cached
new CacheableResponsePlugin({
statuses: [200],
}),
cacheKeyWillBeUsed
],
}),
);
// Cache CSS, JS, and Web Worker requests with a Stale While Revalidate strategy
registerRoute(
// Check to see if the request's destination is style for stylesheets, script for JavaScript, or worker for web worker
({ request }) =>
request.destination === 'style' ||
request.destination === 'script' ||
request.destination === 'worker',
// Use a Stale While Revalidate caching strategy
new StaleWhileRevalidate({
// Put all cached files in a cache named 'assets'
cacheName: 'assets',
plugins: [
// Ensure that only requests that result in a 200 status are cached
new CacheableResponsePlugin({
statuses: [200],
}),
],
}),
);
registerRoute(
/\.(?:png|jpg|jpeg|svg|gif|ico|mp4)$/,
// Use the cache if it's available.
new CacheFirst({
cacheName: "image-cache",
fetchOptions: {
credentials: "include",
},
plugins: [
new ExpirationPlugin({
// Cache only 50 images.
maxEntries: 50,
// Cache for a maximum of a day.
maxAgeSeconds: 24 * 60 * 60,
}),
],
})
)
// Cache the Google Fonts stylesheets with a stale-while-revalidate strategy.
registerRoute(
({ url }) => url.origin === "https://fonts.googleapis.com",
new StaleWhileRevalidate({
cacheName: "google-fonts-stylesheets",
})
)
// Cache the underlying font files with a cache-first strategy for 1 year.
registerRoute(
({ url }) => url.origin === "https://fonts.gstatic.com",
new CacheFirst({
cacheName: "google-fonts-webfonts",
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxAgeSeconds: 60 * 60 * 24 * 365,
maxEntries: 30,
}),
],
})
)
// Catch routing errors, like if the user is offline
setCatchHandler(async ({ event }) => {
// Return the precached offline page if a document is being requested
if (event.request.destination === "document") {
return matchPrecache("/offline.html")
}
return Response.error()
})
addEventListener("message", (event) => {
if (event.data && event.data.type === "SKIP_WAITING") {
skipWaiting()
}
})
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 1
- Comments: 29 (17 by maintainers)
Also, I wanted to let folks know that separate from the issue of how Workbox logs things, we are actively working with the Chrome DevTools team to provide more context about what’s going on during the automatic offline check. Those improvements would help developers regardless of whether they’re using Workbox or not.
Sorry for the delay in getting back to you.
I think that everything is working as “expected,” but unfortunately it is a bit confusing now that the automatic offline navigation detection fires off a navigation request for your
start_url
with the network simulated as offline.I realize that you’re seeing the request failure message logged just because that’s what happens when you use a
NetworkFirst
runtime strategy and you’re offline—there’s an exception thrown when the network request fails, even though a valid response ends up being provided from the cache. So the installability criteria is met, but there’s an error logged.This came up in another GitHub issue recently, and I guess it’s more likely to cause confusion now that the automatic offline navigation detection is fired off by Chrome.
I’m going to leave this issue open to track some work that we could do to improve the logging around a failed network request when using
NetworkFirst
, just to make it clearer that the response is eventually provided. I’m also going to follow-up with the team at Chrome responsible for the automatic offline navigation detection to see if it might be possible to provide better attribution somewhere in DevTools letting developers know that the request is associated with that detection.In addition to the code highlighted above, there’s also console noise (e.g. red error messages) from this code:
https://github.com/GoogleChrome/workbox/blob/2c8bfe7a840567cc4e693cae38441e46a7b0256b/packages/workbox-strategies/src/StrategyHandler.ts#L212-L213
I think we’d have to find a way to avoid both if we want to not scare developers into thinking something is wrong (when it isn’t). Experimenting locally, when I comment out both error logging/throwing, my site doesn’t emit any scary console messages from the offline check.
I think we could potentially remove the re-throw. I remember not being sure about this when I was implementing it, and I chose to keep it to help make sure developers didn’t have errors in their caching logic (or something like that).
If a developer wants to know if their
waitUntil()
code throws, they can check for anerror
property passed to thehandlerDidComplete
callback. (Though they have to be looking for that, it won’t be made obvious in devtools.)We also started seeing “Failed to Fetch” ghost requests errors with Chrome 89 release starting yesterday. These are coming from the new PWA installability checks as reported by Jeff. This is being done to prove that a website that claims to be installable through reporting a link rel=“manifest” is really capable of delivering content while offline. So if you page is not reporting a manifest file, you’ll not get this error reproduced.
I was working earlier to try to get rid of the error message in console, which is misleading, but couldn’t get rid of it. Using a StaleWhileRevalidate strategy to cache the root path is getting the new PWA installability check passing, but the strategy tries to revalidate the file using the original request (which is designed to fail) and throws the error.
I couldn’t find documentation or an easy way to detect this type of Request and avoid trying to send it to network or use it to update cache. It would be nice to have a flag or similar on the Request object indicating it is designed to fail.
Hello @shadowwalker—I’d very much suggest not attempting to avoid DevTools noise via that approach. There’s overhead in re-creating the request, and I don’t really understand what’s going on that would cause it to lead to different logs being displayed.
The Chrome team has heard the feedback from this thread and elsewhere, and rolled back the automatic offline check, starting with Chrome 90. Stay tuned for updates to the public guidance. The check may be reinstated at some point in the future, but the developer experience will be taken into account in anything that gets reimplemented.
Thanks for everyone’s patience regarding this in the meantime!
After chatting with @philipwalton about this, I think the consensus is that making the change outlined in https://github.com/GoogleChrome/workbox/issues/2749#issuecomment-796410150 falls more into the category of “bug fix for unintended behavior” than “breaking change of documented API” so we should be able to get it out sooner in the next Workbox v6 release.
And we can also change that
logger.error()
inStrategyHandler.ts
to alogger.info()
to make it clearer that it might not ultimately be a failure.I hit the same error with a home-grown service-worker and came to “workbox” in the hope it would be over it already.
I don’t get the scary red messages in Firefox. Alternatively removing the site’s manifest (!) avoids generating the messages.
I was having a really hard time understanding why I was seeing your
offline.html
page instead of your index page when manually navigating tohttps://development.docs.coregames.com/?utm_source=web_app_manifest&utm_medium=web_app_manifest&utm_campaign=web_app_manifest
while offline. And the{cacheKeyWillBeUsed}
at least explains that!But yes, the automatic fetch that Chrome performs that’s described at https://developer.chrome.com/blog/improved-pwa-offline-detection/ is still failing. I am now wondering if it might be related to https://github.com/GoogleChrome/workbox/pull/2744, which includes a change to the way some of the asynchronous
NetworkFirst
logic operates.I’d like to see if, once #2744 is merged and we cut the next Workbox release, that error from the automatic offline detection is still logged. In the meantime, I don’t think it should actually affect the functionality of your web app—offline capabilities when you navigate “for real” are working.