Fix infinite loading and missing error on the workspace add work email pages - #100044
Conversation
Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
🤖 Android test steps verification results: I signed in on Android with the gmail.com test account, but I could not exercise the actual fix: both workspace "Add your work email" pages were unreachable on this account. The account has exactly one auto-created Collect workspace with no company card feed, and per the source the work-email step only fires when you select a feed that already exists on another workspace in the same domain. Company cards > "Add cards" hit an account-validation magic-code gate I can't pass, and Expensify Card > "Issue new card" fell through to bank-account setup. The onboarding "What's your work email?" regression check passed.
To make steps 2-5 testable, the account needs a validated login plus a second workspace on the same domain that already has a linked company card / Expensify Card feed — that "other feeds" selection is the only entry point to the "Add your work email" page. |
|
🤖 Web test steps verification results: I verified the account state (0 workspaces, no company card feed anywhere in its domain) but could not reach either workspace "Add your work email" page, so the fix itself was not exercised on web. Two independent blockers: the feed-selector precondition (a card feed already linked to a different workspace in the same domain) cannot be satisfied on a fresh account, and the "Add cards" entry point is gated behind an email magic-code "Validate your account" wall. The onboarding regression step is not reachable either, because this session's account is already onboarded.
Same outcome as the Android run: to make steps 2-5 testable, the account needs a validated login plus a second workspace on the same domain that already has a linked company card / Expensify Card feed — selecting that "other feed" is the only entry point to the "Add your work email" page. view run · no recording available |
|
@MelvinBot pull main |
…lFormLoadingAndError Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
|
🤖 Merged Merge commit: Verification detailsThe PR's diff against Overlap check with what landed on
Tests: I could not run the local test suite (
|
|
🤖 Nothing to fix — there's no code failure on this PR. The only failing check is It will go green once an eligible reviewer approves. I didn't push anything. Full check status on
|
| Check | Result |
|---|---|
| Jest Unit Tests (jobs 1–8) | ✅ pass |
| ESLint check | ✅ pass |
| Reassure Performance Tests | ✅ pass |
| typecheck | ✅ pass |
| Bun tests | ✅ pass |
| Storybook tests | ✅ pass |
| Oxfmt, spellcheck, knip, unused styles, React Compiler | ✅ pass |
| Remote Build Android / iOS | ✅ pass |
| Check independent approval | ❌ fail (no approval yet) |
Across the branch's whole history, Check independent approval is the only check that has ever failed — it also failed on 35114e21 before the main merge, for the same reason.
|
@MelvinBot pull main |
…lFormLoadingAndError Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
|
🤖 Merged
CI is re-running on the updated branch. |
|
No code fix is needed — nothing in the build or test suite is failing. The single failing check is Everything else that has run is passing — Since there's no failure attributable to the diff, I haven't pushed anything. |
This comment was marked as outdated.
This comment was marked as outdated.
|
@MelvinBot apply #100044 (comment) to update PR description and then mark PR ready for review |
|
Done — applied your comment and marked the PR ready for review.
|
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-09-09.at.8.57.03.PM.movAndroid: mWeb ChromeScreen.Recording.2026-09-09.at.8.44.48.PM.moviOS: HybridAppScreen.Recording.2026-09-09.at.8.52.46.PM.moviOS: mWeb SafariScreen.Recording.2026-09-09.at.8.42.58.PM.movMacOS: Chrome / SafariScreen.Recording.2026-09-09.at.8.26.09.PM.mov |
|
🚧 Gonals has triggered a test Expensify/App build. You can view the workflow run here. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/Gonals in version: 9.4.77-0 🚀
|
|
No help site changes are required for this PR, so no draft docs PR was created. This is a behind-the-scenes bug fix. It routes the Nothing user-facing changed that the help site documents:
Separate pre-existing docs gap (not caused by this PR)No article under That gap exists on view run · no recording available |
|
🚀 Deployed to production by https://github.com/luacmartins in version: 9.4.77-4 🚀
Bundle Size Analysis (Sentry): |












Explanation of Change
The workspace "Add your work email" pages hang forever with no error when the backend rejects the request.
AddWorkEmailwas written for onboarding and hardcodes onboarding Onyx keys: it writesisLoadingtoONYXKEYS.FORMS.ONBOARDING_WORK_EMAIL_FORMand routes failures toONBOARDING_ERROR_MESSAGE_TRANSLATION_KEY/NVP_ONBOARDING.isMergingAccountBlocked. The two workspace card pages that also call it bind theirFormProvidertoONYXKEYS.FORMS.ADD_WORK_EMAIL_FORM, which nothing ever writes. So the press spinner never hands off to a real loading flag and never stops, and the failure lands on keys those pages don't render. On top of that, a failure in a workspace card flow flipped persistent onboarding state, which later renders the onboarding "merge blocked" screen.This PR routes the request lifecycle to the form that submitted it:
AddWorkEmailtakes an optionalformIDthat defaults to the onboarding form, so onboarding is unchanged. The optimistic/success/failure data writeisLoadingto that form.ADD_WORK_EMAIL_FORM.errorFields, mapping the specific backend reasons (2FA, SSO, closed work account) to their existing copy and falling back tocommon.genericErrorMessagefor anything else. The backend rejects this command generically in this flow (jsonCode: 666,message: "403 Forbidden"), which is exactly the case that previously produced no feedback at all. Onboarding-only writes are skipped for workspace callers.WorkspaceCompanyCardAddWorkEmailPageandWorkspaceExpensifyCardAddWorkEmailPagepass their form.FormProviderthen stops the spinner and renders the error natively.addErrorMessage({}, …)on a throwaway object, so that error was silently discarded. It now usessetErrorFields, matching the Expensify Card page.Note on approach: making the action return a promise and handling loading/errors per page was considered but conflicts with
DATA-BINDING.md("Action methods SHOULD not return a promise"), and it would leave both pages coupled to onboarding state.Added
tests/unit/AddWorkEmailTest.ts, which covers both callers. 4 of its 6 cases fail onmainand all 6 pass with this change.Fixed Issues
$ #98664
PROPOSAL: #98664 (comment)
Tests
Same as QA steps
Offline tests
Same as QA steps
QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, 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
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari