You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2026-06-17] [$250] Currency RHP doesn't open when pressing "Update workspace currency" #93062
Version Number: 9.4.1-4 Reproducible in staging?: Y Reproducible in production?: N If this was caught during regression testing, add the test name, ID and link from BrowserStack: Email or phone of affected tester (no customers): Logs:https://stackoverflow.com/c/expensify/questions/4856 Expensify/Expensify Issue URL: Issue reported by:@bernhardoj Slack conversation (hyperlinked to channel name): #Expensify Bugs
Action Performed:
Prerequisite: Have a workspace with non-supported currency for business bank account
Go to the workspace workflows page
Scroll down to the Payment section
Press "+ Add bank account"
Press "Update workspace currency"
Expected Result:
Currency RHP opens to update the currency
Actual Result:
No action is happening when clicking on "Update workspace currency",
Workaround:
Unknown
Platforms:
Select the officially supported platforms where the issue was reproduced:
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
Identify the pull request that introduced this issue and revert it.
The "Update workspace currency" button does fire its confirm handler — but the navigation it triggers gets silently undone by the global confirm modal's back-navigation handling.
The handler confirmCurrencyChangeAndHideModal navigates deferred, via setNavigationActionToMicrotaskQueue (a microtask + requestAnimationFrame).
The confirm modal is opened through useConfirmModal, which sets shouldHandleNavigationBack: true. When such a modal hides, Modal.hideModal schedules a window.history.back() on setTimeout(0), guarded so it only fires if no navigation has pushed a new history entry in the meantime (line 72).
That guard assumes the confirm callback navigates synchronously. Because the Workflows handler defers its navigation past the setTimeout(0), the guard sees no new history entry, so history.back() runs and cancels the forward navigation → nothing happens.
Why the sibling flow works, and why this is staging-only
The equivalent WorkspaceInvoiceVBASection navigates to the sameWORKSPACE_OVERVIEW_CURRENCY route synchronously inside its .then(). That pushes a history entry before the setTimeout(0) fires, so the guard short-circuits and the navigation sticks. This is why it isn't broken.
What changes do you think we should make in order to solve the problem?
In confirmCurrencyChangeAndHideModal, navigate directly (synchronously inside the .then()) instead of wrapping it in setNavigationActionToMicrotaskQueue — exactly as the working invoice section does. This restores the synchronous-navigation contract the modal's guard relies on.
Before merging, confirm this doesn't reintroduce the original navigation issue from #76691 that the microtask wrapper was added for. The invoice sibling navigating directly to the identical route suggests direct navigation is safe.
What alternative solutions did you explore? (Optional)
If a revert is preferred for the deploy block, identify and revert the recent modal back-navigation change that introduced the guarded deferred history.back() in Modal/index.tsx (likely the modal refactor merged into this staging cut). The targeted one-line fix above is lower-risk.
Confidence
High on the failure mechanism and code location (verified against Modal/index.tsx:63-80, useConfirmModal.ts:17, and the working invoice sibling). Medium on exactly which recent modal-infra PR is the trigger — worth a quick runtime confirm (e.g. a breadcrumb in the rAF callback + watching popstate).
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/93062) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
Classification: Frontend bug Causing PR: #77919 - "Refactor ConfirmModal usage to useConfirmModal in workspace accounting and general pages (Part 1)" by @thelullabyy (Medium confidence) Related Issues: None
Recommendation: NEEDS INVESTIGATION
The root cause is not fully confirmed through static analysis alone, but PR #77919 is the strongest candidate — it is the only staging-only change that directly modified ConfirmModalWrapper.tsx and ModalContext.tsx, which are used by WorkspaceWorkflowsPage's confirm modal flow. The author and reviewers should verify whether the new isConfirmLoading !== undefined check or the resolveModal addition interacts incorrectly with the "Update workspace currency" flow.
The bug affects WorkspaceWorkflowsPage.tsx — specifically the "+ Add bank account" button, which calls showConfirmModal(...) when the workspace has an unsupported currency for global reimbursement.
consthandleConfirm=()=>{setCloseAction(ModalActions.CONFIRM);if(props.isConfirmLoading!==undefined){// NEW in PR 77919setIsConfirmLoading(true);resolveModal({action: ModalActions.CONFIRM});}else{setIsVisible(false);// Only this path existed before}};
For WorkspaceWorkflowsPage, showConfirmModal is called without isConfirmLoading, so props.isConfirmLoading === undefined, and the else branch should run. However, there may be subtle interactions with the newly-added resolveModal mechanism or state management.
PR Refactor ConfirmModal usage to useConfirmModal in workspace accounting and general pages (Part 1) #77919 also changed WorkspaceOverviewPage.tsx to pass isConfirmLoading: isPendingDelete where isPendingDelete can be false (not undefined). When isConfirmLoading={false} is passed, false !== undefined is true, which takes the resolveModal path — this means the confirm button keeps the modal open and shows a spinner even when no deletion is pending. This could be a related issue.
The bug is reproducible on Windows: Chrome and MacOS: Chrome/Safari (web), not iOS/Android. This suggests the issue may relate to web-specific modal behavior.
The WorkspaceWorkflowsPage uses useConfirmModal to show a "Workspace currency not supported" confirm modal when "+ Add bank account" is pressed. When the user clicks "Update workspace currency" (the confirm button), the expected flow is:
Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW_CURRENCY...)) is called
PR #77919 introduced new state (isConfirmLoading, resolveModal) into this flow. While theoretically the else branch should still run for this case, there may be a timing or state issue introduced by the PR that breaks the promise resolution or navigation in the web context. The author/reviewers of PR #77919 are best positioned to confirm whether the changes interact unexpectedly with the WorkspaceWorkflowsPage confirm modal flow.
Recommended action: Revert PR #77919 and verify whether the bug is fixed. If reverting is too disruptive (due to other PRs depending on the new isConfirmLoading mechanism), a targeted fix should be applied to ensure ConfirmModalWrapper.handleConfirm closes the modal correctly when props.isConfirmLoading is not passed.
@thelullabyy can you please accept the job below? Please reply here and tag me once you have. If you're not sent an offer in Upwork when you're assigned to an issue, please tag me. If an Upwork job exists, please submit a proposal. https://www.upwork.com/jobs/~022064410233541249702
^ Test case created. Thx
@mallenexpensify
I suppose only I need a payment
Since Melvin was the author of the PR
Activity
melvin-bot commentedon Jun 9, 2026
Triggered auto assignment to @pecanoro (
DeployBlockerCash), see https://stackoverflowteams.com/c/expensify/questions/9980/ for more details.melvin-bot commentedon Jun 9, 2026
💬 A slack conversation has been started in #expensify-open-source
github-actions commentedon Jun 9, 2026
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
MelvinBot commentedon Jun 9, 2026
Proposal
What is the root cause of that problem?
The "Update workspace currency" button does fire its confirm handler — but the navigation it triggers gets silently undone by the global confirm modal's back-navigation handling.
The handler
confirmCurrencyChangeAndHideModalnavigates deferred, viasetNavigationActionToMicrotaskQueue(a microtask +requestAnimationFrame).The confirm modal is opened through
useConfirmModal, which setsshouldHandleNavigationBack: true. When such a modal hides,Modal.hideModalschedules awindow.history.back()onsetTimeout(0), guarded so it only fires if no navigation has pushed a new history entry in the meantime (line 72).That guard assumes the confirm callback navigates synchronously. Because the Workflows handler defers its navigation past the
setTimeout(0), the guard sees no new history entry, sohistory.back()runs and cancels the forward navigation → nothing happens.Why the sibling flow works, and why this is staging-only
WorkspaceInvoiceVBASectionnavigates to the sameWORKSPACE_OVERVIEW_CURRENCYroute synchronously inside its.then(). That pushes a history entry before thesetTimeout(0)fires, so the guard short-circuits and the navigation sticks. This is why it isn't broken.setNavigationActionToMicrotaskQueuewrapper itself is old (added in PR fix: navigation issue in workspace workflow navigation #82499 for issue [Due for payment 2026-04-17] Refactor ConfirmModal usage to useConfirmModal in workspace feature pages #76691), so it was in production. The regression is the interaction with the recently-changed modal back-handling (the deferred, guardedhistory.back()inModal/index.tsx). That recent infra change is what makes the deferred navigation newly fragile in staging.What changes do you think we should make in order to solve the problem?
In
confirmCurrencyChangeAndHideModal, navigate directly (synchronously inside the.then()) instead of wrapping it insetNavigationActionToMicrotaskQueue— exactly as the working invoice section does. This restores the synchronous-navigation contract the modal's guard relies on.Before merging, confirm this doesn't reintroduce the original navigation issue from #76691 that the microtask wrapper was added for. The invoice sibling navigating directly to the identical route suggests direct navigation is safe.
What alternative solutions did you explore? (Optional)
history.back()inModal/index.tsx(likely the modal refactor merged into this staging cut). The targeted one-line fix above is lower-risk.Confidence
High on the failure mechanism and code location (verified against
Modal/index.tsx:63-80,useConfirmModal.ts:17, and the working invoice sibling). Medium on exactly which recent modal-infra PR is the trigger — worth a quick runtime confirm (e.g. a breadcrumb in the rAF callback + watching popstate).Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/93062)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
OSBotify commentedon Jun 9, 2026
🔍 Investigation Summary
Classification: Frontend bug
Causing PR: #77919 - "Refactor ConfirmModal usage to useConfirmModal in workspace accounting and general pages (Part 1)" by @thelullabyy (Medium confidence)
Related Issues: None
Recommendation: NEEDS INVESTIGATION
The root cause is not fully confirmed through static analysis alone, but PR #77919 is the strongest candidate — it is the only staging-only change that directly modified
ConfirmModalWrapper.tsxandModalContext.tsx, which are used byWorkspaceWorkflowsPage's confirm modal flow. The author and reviewers should verify whether the newisConfirmLoading !== undefinedcheck or theresolveModaladdition interacts incorrectly with the "Update workspace currency" flow.Assigned: @thelullabyy (PR author), @ZhenjaHorbach @roryabraham (approving reviewers) from highest-confidence causing PR
Labels:
DeployBlockerCashkept (frontend App PR is suspected cause, staging-only)📋 Detailed Analysis
Evidence
WorkspaceWorkflowsPage.tsx— specifically the "+ Add bank account" button, which callsshowConfirmModal(...)when the workspace has an unsupported currency for global reimbursement.ConfirmModalWrapper.tsx(the component wrapping everyuseConfirmModalconfirm modal) andModalContext.tsx.isConfirmLoadingstate and changinghandleConfirmfrom unconditionally callingsetIsVisible(false)to a conditional path:WorkspaceWorkflowsPage,showConfirmModalis called withoutisConfirmLoading, soprops.isConfirmLoading === undefined, and theelsebranch should run. However, there may be subtle interactions with the newly-addedresolveModalmechanism or state management.WorkspaceOverviewPage.tsxto passisConfirmLoading: isPendingDeletewhereisPendingDeletecan befalse(notundefined). WhenisConfirmLoading={false}is passed,false !== undefinedistrue, which takes theresolveModalpath — this means the confirm button keeps the modal open and shows a spinner even when no deletion is pending. This could be a related issue.Verification
src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsxshowConfirmModal(fromuseConfirmModal) at line ~678 for the "Update workspace currency" confirm modalconfirmCurrencyChangeAndHideModal()in the.then()callbacksrc/components/Modal/Global/ConfirmModalWrapper.tsxandsrc/components/Modal/Global/ModalContext.tsxWorkspaceWorkflowsPage.tsxwas also modified by PRs Hide Payer row during incomplete bank account setup #90538 and Fix agent approver row flicker on reconnect after adding agent offline #92738 in staging, but those changes are unrelated to the currency confirmation flow (payer row visibility and agent flicker fix respectively)Root Cause
The
WorkspaceWorkflowsPageusesuseConfirmModalto show a "Workspace currency not supported" confirm modal when "+ Add bank account" is pressed. When the user clicks "Update workspace currency" (the confirm button), the expected flow is:ConfirmModalWrapper.handleConfirm()setsisVisible = falsehandleModalHide()fires, callscloseModal({action: CONFIRM}).then()runsconfirmCurrencyChangeAndHideModal()Navigation.setNavigationActionToMicrotaskQueue(() => Navigation.navigate(ROUTES.WORKSPACE_OVERVIEW_CURRENCY...))is calledPR #77919 introduced new state (
isConfirmLoading,resolveModal) into this flow. While theoretically theelsebranch should still run for this case, there may be a timing or state issue introduced by the PR that breaks the promise resolution or navigation in the web context. The author/reviewers of PR #77919 are best positioned to confirm whether the changes interact unexpectedly with theWorkspaceWorkflowsPageconfirm modal flow.Recommended action: Revert PR #77919 and verify whether the bug is fixed. If reverting is too disruptive (due to other PRs depending on the new
isConfirmLoadingmechanism), a targeted fix should be applied to ensureConfirmModalWrapper.handleConfirmcloses the modal correctly whenprops.isConfirmLoadingis not passed.22 remaining items
melvin-bot commentedon Jun 16, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)melvin-bot commentedon Jun 16, 2026
Payment Summary
Upwork Job
BugZero Checklist (@mallenexpensify)
mallenexpensify commentedon Jun 18, 2026
Payment Summary
Contributor+: @ZhenjaHorbach due $250 via NewDot
^ Test case created. Thx
ZhenjaHorbach commentedon Jun 18, 2026
@mallenexpensify
I suppose only I need a payment
Since Melvin was the author of the PR
thelullabyy commentedon Jun 18, 2026
No need payment for me here
mallenexpensify commentedon Jun 19, 2026
Thx, payment summary updated
garrettmknight commentedon Jun 19, 2026
$250 approved for @ZhenjaHorbach