Repository navigation
[NoQA] Retry failed locale chunk loads to prevent a permanently stuck boot splash - #97491
Conversation
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
@ZhenjaHorbach Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
I'll be OOO next week (back Aug 10) 🌴 |
|
@ZhenjaHorbach can you review please |
JmillsExpensify
left a comment
There was a problem hiding this comment.
No product review required on this one.
|
I will start today and complete today or Monday morning! |
Reviewer Checklist
Screenshots/VideosAndroid: HybridApp2026-08-03.12.45.18.movAndroid: mWeb Chrome2026-08-03.12.45.18.moviOS: HybridApp2026-08-03.12.40.05.moviOS: mWeb Safari2026-08-03.12.41.21.movMacOS: Chrome / Safari2026-08-03.12.36.44.mov |
|
LGTM! |
|
@mountiny |
mountiny
left a comment
There was a problem hiding this comment.
Nice this one has been quite tricky for the users, thanks for the fix!
|
🚧 mountiny has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
Hi @VickyStash. Can you please explain how to test the console steps here? Or should they be tested internally?
|
|
🚀 Deployed to staging by https://github.com/mountiny in version: 9.4.52-0 🚀
|
|
🤖 No help site changes are required for this PR. I reviewed the changes and this PR is an entirely internal, technical fix with no user-facing surface that maps to any article under Why no docs changes are neededThe PR adds an error-recovery mechanism for failed locale bundle chunk loads. When a locale chunk 404s (typically a stale app shell after a deploy requesting a chunk hash that no longer exists), the app was left permanently stuck on the boot splash. The fix:
Files changed are all internal code/tests:
There is no new or changed feature, setting, tab, button, workflow, or product behavior that a help site reader would need documented. Help articles cover customer-facing product functionality, and nothing here changes that. @VickyStash — no draft help site PR was created since no documentation updates are needed. If you believe a specific article should be updated as a result of this change, let me know which one and I'll take another look. |
@IuliiaHerets, It's not possible to follow these steps in staging, that's why in QA steps section I've mentioned more realistic steps, try to follow them please |
|
We will monitor in quality |
|
🚀 Deployed to production by https://github.com/roryabraham in version: 9.4.52-11 🚀
Bundle Size Analysis (Sentry): |

Explanation of Change
On web, when the locale bundle chunk fails to load (typically a stale app shell after a deploy requesting a chunk hash that no longer exists —
ChunkLoadError: Loading chunk NNNN failed. (missing: …)), the app is left permanently stuck on the full-screen green boot splash:IntlStoreare plain dynamic imports, so unlike the lazy component chunks (covered bylazyRetrysince Clear stale SW caches before reloading on ChunkLoadError #92339) a failed locale chunk rejected with nothing catching it.areTranslationsLoadingstayedtrueforever andcurrentLocalewas never set, sopreferredLocalenever resolved.Expensify.tsxrequires a resolved locale (shouldInit = … && !!preferredLocale), soBootSplash.hide()was never called.Fix:
lazyRetryinto a new genericretryDynamicImport(behavior unchanged for the three component call sites): first failure → plain reload; second failure that is aChunkLoadErrorwhile online → clear service worker caches and reload; anything else → reject.IntlStore.loadthrough it, with retry state scoped per locale (locale:<locale>).sessionStorageaccess in the ladder: if the retry state cannot be read or written (e.g. Safari private browsing), reject instead of reloading — otherwise attempts cannot be counted and the page would reload forever.LocaleTranslationsLoadspan used to end cleanly in.finally, reporting a failed load as a fast successful one. It now ends withfailed: trueon error.locale-load-failed(with the locale inextra). The splash intentionally stays up in that terminal state — with no translations in memory, any screen we could show would render raw translation keys.Fixed Issues
$ #97309
PROPOSAL: N/A
Tests
Temporary snippet in
IntlStore.load— replaceloaderPromisein theretryDynamicImportcall:Then in the browser console (clear the
RETRY_LAZY_REFRESHED:locale:<locale>sessionStorage key between runs):sessionStorage.setItem('debugLocaleFail', '2')+ reload → must plain-reload, cache-clear-reload, then boot normally on the third attempt (recovery path).sessionStorage.setItem('debugLocaleFail', '99')+ reload → three attempts, then the splash stays and the error is captured to Sentry (exhausted path).sessionStorage.removeItem('debugLocaleFail')→ off.Offline tests
Same, as in Tests section
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)Avatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
MacOS: Chrome / Safari
normal.mp4
send_error_to_sentry.mp4
reload.mp4