Skip to content

fix: serve precached files when network-first requests fail offline - #26624

Closed
totally-not-ai[bot] wants to merge 6 commits into
mainfrom
fix/sw-network-first-precache-fallback
Closed

totally-not-ai[bot] wants to merge 6 commits into
mainfrom
fix/sw-network-first-precache-fallback

Conversation

@totally-not-ai

@totally-not-ai totally-not-ai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Review risk: two-way door — one fallback in the generated service worker; affects only requests that already fail on the network.

Behavior deviation · flow-server (service worker) · apps with @PWA running in development mode with the dev bundle

Background — service worker routes. Workbox serves a request with the first route that matches it. A route registered before precacheAndRoute takes precedence over the precache, and a NetworkFirst strategy only falls back to the runtime cache, which is filled by requests the service worker has already handled.

In development mode with the dev bundle, reloading a @PWA app while offline after the first visit shows a blank page instead of the offline page. The bundle scripts are precached, but were never served from the precache. Opening the app a second time while online was the only workaround.

Risks:

  • ⚠️ Behaviour change: a network-first request that fails is now answered from the precache when the file is precached there. Before, it failed unless the runtime cache had a copy.
  • ✅ No API, security, serialization, threading, memory or migration impact; production mode is unaffected, since it has no network-first route for precached files.

Context. The dev-mode /VAADIN/* network-first route was added for the Vite dev server, whose files are not precached. The dev bundle's files are precached, so on the first offline reload every bundle script failed and the app shell never started, and the offline stub could not be shown.

  • Added a Workbox plugin to the shared NetworkFirst strategy that returns matchPrecache(request) from handlerDidError, so a failed request falls back to the precache after the runtime cache.
  • Added a dev bundle variant of the PWA test module (test-pwa/pom-devbundle.xml), following the existing test-themes/pom-devbundle.xml, so the PWA tests also run without hotdeploy.
  • Added a PWA test that fetches every precached bundle file offline without fetching it online first. It fails without the fix in the dev bundle variant, passes in production, and is skipped with hotdeploy, where nothing is precached.

Working on behalf of Artur

In development mode with a dev bundle, the service worker routes
/VAADIN/* through a network-first strategy that is registered before the
precache route. When offline, that strategy only looks in the runtime
cache, which is empty until the service worker has handled a request
once. The dev bundle scripts are precached, but were never served from
the precache, so reloading offline after the first visit left a blank
page instead of the offline page.

Fall back to the precache when a network-first request fails.
@totally-not-ai

Copy link
Copy Markdown
Contributor Author

Type of change

  • Bugfix

How to test

  1. Create an app with only @PWA(name = "…", shortName = "…") on its AppShellConfigurator and a server-side @Route, running in development mode with the dev bundle (vaadin.frontend.hotdeploy=false, the default).
  2. Open it in Chrome in a fresh profile, wait until the service worker is active, then tick Offline in DevTools > Network.
  3. Reload the page.
  4. The app shell shows the default "You are offline" page in an iframe. Without the change, the page is blank and the console shows no-response errors for the /VAADIN/build/*.js files.

Add a dev bundle variant of the PWA test module, and a test that fetches
the precached bundle files offline without fetching them online first,
so that only the precache can serve them.
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

 1 676 files  +1   1 762 suites  +1   2h 27m 56s ⏱️ + 6m 9s
13 134 tests +7  13 064 ✅ +5  70 💤 +2  0 ❌ ±0 
13 768 runs  +7  13 698 ✅ +5  70 💤 +2  0 ❌ ±0 

Results for commit 29ebe97. ± Comparison against base commit f24bc43.

♻️ This comment has been updated with latest results.

@Artur-
Artur- marked this pull request as ready for review October 22, 2026 12:55

@vaadin-review-bot vaadin-review-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes — left 1 comment.

Finding
🧪 The only test that exercises the fix can silently skip itself.

// The dev bundle and the production bundle are precached, while the
// frontend served by the Vite dev server is not
List<String> bundleUrls = getPrecachedBundleUrls();
Assume.assumeFalse("No precached bundle files", bundleUrls.isEmpty());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 The only test that exercises the fix can silently skip itself.

testPrecachedBundleOffline is the sole guard for this change: it is skipped in hotdeploy and passes in production with or without the fix, so only the new dev-bundle module proves anything. The skip condition is derived from the very thing being measured — if /VAADIN/build/ ever stops matching the precache keys (assets dir renamed, manifest injection broken), or the inline script throws and hits .catch(() => resolve([])), the test turns green everywhere and the fix loses its coverage with no signal.

Keying the skip on the configuration (hotdeploy) rather than on an empty precache would keep it honest.

PwaTestIT.java:250 · tests

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed in 877d934. The test now skips based on the configuration: test-pwa/pom.xml passes vaadin.frontend.hotdeploy to failsafe, and the test skips only when that property is true. The dev bundle and production variants don't set it, so there an empty precache now fails with "Expected precached bundle files" instead of skipping. The inline script also no longer turns errors into []. It returns the error text, and the test fails with "Reading the precache failed: …". Verified: skipped with hotdeploy, passes in production and with the dev bundle. Against a flow-server without the fix, the dev bundle run still fails as expected.

Skipping when no precached bundle files were found let the test pass
silently if the precache stopped matching or could not be read. Skip
on the hotdeploy configuration instead, and fail when the precache is
empty or reading it throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

📦 Snapshot published

25.4.sw-network-first-precache-fallback-SNAPSHOT — built from 29ebe97 (run).

Built without running tests. Look at the checks on this pull request before relying on it.

Every new commit on this branch republishes it while the snapshot build label is there. Add -U to pick the newest build up.

How to use it
<repository>
  <id>vaadin-snapshots</id>
  <url>https://maven.vaadin.com/vaadin-prereleases</url>
  <snapshots><enabled>true</enabled></snapshots>
</repository>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.vaadin</groupId>
      <artifactId>flow-bom</artifactId>
      <version>25.4.sw-network-first-precache-fallback-SNAPSHOT</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <!-- After flow-bom, so that its Flow version wins -->
    <dependency>
      <groupId>com.vaadin</groupId>
      <artifactId>vaadin-bom</artifactId>
      <version>25.4-SNAPSHOT</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Replace the application's own vaadin-bom import with these two rather than adding them next to it: with two vaadin-bom imports, the first one decides the platform version. flow-bom has to come before vaadin-bom: Maven takes the first version it finds for an artifact, so the other order leaves the platform's own Flow version in use. 25.4-SNAPSHOT is the unreleased platform snapshot of the same minor version as this one, so this also moves the application off its released platform version.

@sonarqubecloud

Copy link
Copy Markdown

…precache-fallback

# Conflicts:
#	flow-tests/test-pwa/pom-devbundle.xml
@totally-not-ai

Copy link
Copy Markdown
Contributor Author

Closing as superseded by #24807, which already serves precached dev-bundle files from the precache instead of the network-first route, so this fallback is no longer needed.

@totally-not-ai totally-not-ai Bot closed this Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants