Problem
On laptop wake the browser tab loses network for roughly 30 seconds, and two lazy loads that fail inside that window never recover once the network returns. The AppNavigator chunk fails with a ChunkLoadError, lazyRetry burns through its retry ladder while still offline, and the rejection reaches the root error boundary — leaving the error page up until the user manually refreshes. The locale chunk fails in the same window, and because IntlStore.load() had no catch, the translation cache stayed empty and translate() returned raw keys such as the reported genericErrorPage.title. usePageRefresh compounded it: during a flap navigator.onLine still reports true while getIsOffline() reports offline, so the Refresh button would clear the service worker cache holding the only app shell available while the network was down.
Solution
Both loads now park instead of failing. lazyRetry checks getIsOffline() before its retry ladder and, when offline, waits on NetworkState.onReachabilityConfirmed and re-runs the import, re-arming the wait on a second offline failure; the promise stays pending so Suspense keeps the splash screen up and the error boundary never renders. IntlStore.load() gains the same offline park-and-retry loop via a recursive loadWithOfflineRetry, with NetworkState imported dynamically since IntlStore is imported almost everywhere and a static import would pull the network stack onto graphs that never fetch a locale. Because the offline path consumes no retry state, a blip no longer burns the plain-reload attempt or clears the service worker cache — cache clearing stays reserved for the online post-deploy stale-shell case — and usePageRefresh switches from navigator.onLine to getIsOffline() so its Refresh button cannot wipe that cache mid-flap.
PR
#97878
Issue Owner
Current Issue Owner: @adhorodyski
Upwork Automation - Do Not Edit
Problem
On laptop wake the browser tab loses network for roughly 30 seconds, and two lazy loads that fail inside that window never recover once the network returns. The
AppNavigatorchunk fails with aChunkLoadError,lazyRetryburns through its retry ladder while still offline, and the rejection reaches the root error boundary — leaving the error page up until the user manually refreshes. The locale chunk fails in the same window, and becauseIntlStore.load()had nocatch, the translation cache stayed empty andtranslate()returned raw keys such as the reportedgenericErrorPage.title.usePageRefreshcompounded it: during a flapnavigator.onLinestill reportstruewhilegetIsOffline()reports offline, so the Refresh button would clear the service worker cache holding the only app shell available while the network was down.Solution
Both loads now park instead of failing.
lazyRetrychecksgetIsOffline()before its retry ladder and, when offline, waits onNetworkState.onReachabilityConfirmedand re-runs the import, re-arming the wait on a second offline failure; the promise stays pending so Suspense keeps the splash screen up and the error boundary never renders.IntlStore.load()gains the same offline park-and-retry loop via a recursiveloadWithOfflineRetry, withNetworkStateimported dynamically sinceIntlStoreis imported almost everywhere and a static import would pull the network stack onto graphs that never fetch a locale. Because the offline path consumes no retry state, a blip no longer burns the plain-reload attempt or clears the service worker cache — cache clearing stays reserved for the online post-deploy stale-shell case — andusePageRefreshswitches fromnavigator.onLinetogetIsOffline()so its Refresh button cannot wipe that cache mid-flap.PR
#97878
Issue Owner
Current Issue Owner: @adhorodyskiUpwork Automation - Do Not Edit