fix: never blank the app when a deploy strands an open tab - #97
Merged
Conversation
Every view but the learner is lazy loaded, so a tab left open across a
deploy asks for chunk filenames the server no longer has. The dynamic
import rejects, and with no error boundary anywhere React unmounted the
whole tree. The user got a white page with nothing saying that a reload
fixes it. A facilitator hitting that mid-workshop has no way to know.
Reproduced by loading the built app, removing the facilitator chunk to
stand in for a deploy, then navigating to it:
blank? YES
TypeError: Failed to fetch dynamically imported module:
/assets/FacilitatorDashboard-BC0Lt40f.js
A boundary now wraps the screens. A stale-chunk error reloads once,
which is the actual fix, and anything else shows a panel explaining what
happened with a reload button, because a message always beats a blank
page.
The automatic reload is guarded by a sessionStorage flag so a genuinely
broken deploy cannot put the tab in a reload loop, which would be worse
than the blank page since the user could not even read the error. The
flag clears 30 seconds after a successful mount rather than on mount: a
crash loop fails long before that, so the guard survives to show the
panel, while a tab that recovered regains its retry for the next deploy.
Also fixed in the service worker, which cached any response for
/assets/*, including the index.html that the SPA rewrite returns for a
missing chunk. That would pin an HTML body under a .js URL for the life
of the cache, so the failure would outlive the reload meant to fix it.
Only successful responses are cached now, in both handlers, and VERSION
rotates to v3 since the caching logic changed.
Verified against the same repro, both paths: a fresh tab auto-reloads
and recovers, and a tab whose retry is spent shows the panel. Neither
goes blank. Screenshotted the panel, which caught the button rendering
white-on-cream because I had used var(--accent) and var(--fg), neither
of which exists in this theme; the accent is var(--bitcoin). Fixed there
and in the review archive added earlier.
Full suite green: 30 unit, 30 integration, e2e all nine files, tsc clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is what made the facilitator dashboard show blank. Not a broken build: production and local both render it fine in a clean browser. The problem was an already-open tab.
Every view but
LearnerViewis lazy loaded. A tab open across a deploy asks Vercel for chunk filenames from the old build, which no longer exist there. The dynamic import rejects, and with no error boundary anywhere in the app, React unmounted the whole tree.Reproduced by loading the built app, removing the facilitator chunk to stand in for a deploy, then navigating to it:
The fix
A boundary wraps the screens. A stale-chunk error reloads once, which is the actual fix, and anything else shows a panel explaining what happened with a reload button. A message always beats a blank page.
The auto-reload is guarded by a
sessionStorageflag so a genuinely broken deploy cannot put the tab in a reload loop, which would be worse than the blank page since the user could not even read the error. The flag clears 30 seconds after a successful mount rather than on mount: a crash loop fails long before that, so the guard survives to show the panel, while a tab that recovered regains its retry for the next deploy.Service worker bug found alongside
/assets/*cached any response, including theindex.htmlthat the SPA rewrite returns for a missing chunk. That would pin an HTML body under a.jsURL for the life of the cache, so the failure would outlive the reload meant to fix it. Only successful responses are cached now, in both handlers, andVERSIONrotates to v3 since the caching logic changed.Verification
Same repro, both paths:
Screenshotting the panel caught a second bug of my own: the button rendered white-on-cream because I had used
var(--accent)andvar(--fg), neither of which exists in this theme. The accent isvar(--bitcoin). Fixed there and in the review archive added in #96.Full suite green: 30 unit, 30 integration, all nine e2e files,
tsc --noEmitclean.🤖 Generated with Claude Code