Skip to content

[Due for payment 2026-05-26] [Due for payment 2026-05-26] Expensify Card - Error page briefly shown when deactivate Expensify Card #90866

Closed

Description

@jponikarchuk

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.3.75-1
Reproducible in staging?: Yes
Reproducible in production?: No
If this was caught during regression testing, add the test name, ID and link from BrowserStack: #88603
Email or phone of affected tester (no customers): duytestnguyen+222@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: Windows 11 Home/Chrome
App Component: Workspace Settings

Action Performed:

Preconditions:

  • An account with Workspace, enable the Expensify card
  • Assign a card to a member
  1. Open https://staging.new.expensify.com/
  2. Navigate to Members
  3. Open the member details with assigned Expensify card
  4. Open the assigned Expensify card
  5. Scroll down and press Deactivate card
  6. Press Deactivate

Expected Result:

Verify that the modal closes correctly and the navigation happens after the closing animation has finished without error page shown

Actual Result:

An error page shown briefly

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari

Screenshots/Videos

Video.mp4

View all open jobs on GitHub

Issue OwnerCurrent Issue Owner: @Pujan92

Activity

  1. added
    DeployBlockerCashThis issue or pull request should block deployment
    BugSomething is broken. Auto assigns a BugZero manager.
    on May 16, 2026
  2. melvin-bot commented on May 16, 2026

    @melvin-bot

    You have been assigned to this deploy blocker because you recently merged this PR: #88603

  3. melvin-bot commented on May 16, 2026

    @melvin-bot

    💬 A slack conversation has been started in #expensify-open-source

  4. github-actions commented on May 16, 2026

    @github-actions
    Contributor

    👋 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:

    1. Identify the pull request that introduced this issue and revert it.
    2. Find someone who can quickly fix the issue.
    3. Fix the issue yourself.
  5. OSBotify commented on May 16, 2026

    @OSBotify
    Contributor

    Investigation Summary

    Classification: Frontend bug
    Causing PR: #88603 - "Migrate InteractionManager - batch 1" by @mrejdak (High confidence)
    Related Issues: None

    Recommendation: REVERT

    The PR introduced a timing regression in the Expensify Card deactivation flow. The fix needs to happen in the App repo by either reverting the PR or applying a targeted fix to restore the navigation-after-animation-completes behavior.

    Assigned: @mrejdak (PR author), @collectioneur, @Pujan92, @roryabraham (approving reviewers)
    Labels: DeployBlockerCash kept (Frontend bug in App). No label changes made.

    Detailed Analysis

    Evidence

    PR #88603 migrated WorkspaceExpensifyCardDetailsPage.tsx away from InteractionManager and the old ConfirmModal pattern to the new useConfirmModal / showConfirmModal promise-based API.

    The bug is staging-only (not reproducible in production), and PR #88603 was merged on 2026-05-14 and is included in the current 9.3.75-1 staging deploy checklist.

    The issue is directly triggered by the deactivation flow change in this PR.

    Verification

    • File affected: src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx
    • PR Migrate InteractionManager - batch 1 #88603 modifies this exact file (confirmed via gh pr view 88603 --json files)
    • The diff shows the deactivation logic was rewritten to use showConfirmModal and Navigation.goBack() is now called immediately after the promise resolves

    Root Cause

    Old behavior (before PR #88603):
    The ConfirmModal was rendered inline with shouldSetModalVisibility={false} and navigation was triggered via the onModalHide callback, which fires only after the modal's closing animation has fully completed. This guaranteed that Navigation.goBack() was never called while the modal was still animating closed.

    New behavior (after PR #88603):
    deactivateCard() is now an async function that awaits showConfirmModal(...). When the user confirms, ConfirmModalWrapper.handleConfirm() sets isVisible = false (starting the close animation) and then onModalHide fires — which calls closeModal(), resolving the promise. At that point, the await resumes and Navigation.goBack() is called.

    The issue is that the promise resolves at the start of the onModalHide callback, but the modal's closing animation may not be fully complete at that point when shouldSetModalVisibility={false} is passed. Navigating away while the modal overlay is still visible causes the screen to briefly render an error page before the modal finishes closing.

    The original code explicitly used onModalHide to defer navigation until after the animation. The new code loses this guarantee because Navigation.goBack() is called synchronously after the promise resolves inside handleModalHide, which can fire before the underlying animation is truly done.

  6. MelvinBot commented on May 16, 2026

    @MelvinBot
    Contributor

    Proposal

    What is the root cause of that problem?

    PR #88603 changed the card deactivation flow in WorkspaceExpensifyCardDetailsPage.tsx. The old code used InteractionManager.runAfterInteractions to defer deactivateCardAction until after animations completed, and triggered Navigation.goBack() via the modal's onModalHide callback — so navigation happened before the card data was deleted from Onyx.

    The new code calls deactivateCardAction before Navigation.goBack(). Since deactivateCardAction optimistically sets the card to null in Onyx, React processes that state change and re-renders the component with card=null before goBack() removes the screen from the navigation stack. This triggers the NotFoundPage guard at line 221, causing the brief error page flash.

    What changes do you think we should make in order to solve the problem?

    Swap the order at lines 136-137: call Navigation.goBack() before deactivateCardAction(defaultFundID, card). This restores the old semantics where navigation away starts before the card data is removed from Onyx, preventing the NotFoundPage flash.

    What alternative solutions did you explore? (Optional)

    An alternative would be to add a "navigating away" guard that suppresses the NotFoundPage render when a goBack is already in progress, but swapping the call order is simpler and directly matches the pre-regression behavior.

    Investigation details

    Data flow causing the bug:

    1. User confirms deactivation → showConfirmModal promise resolves
    2. deactivateCardAction(defaultFundID, card) runs — optimistically sets card to null in Onyx
    3. Navigation.goBack() runs — but React processes the Onyx update first
    4. Component re-renders with card=null → NotFoundPage flashes
    5. Navigation animation completes and removes the screen

    Old code (before PR #88603):

    const deactivateCard = () => {
        setIsDeactivateModalVisible(false);
        shouldGoBack.current = true;
        InteractionManager.runAfterInteractions(() => {
            deactivateCardAction(defaultFundID, card);
        });
    };
    // goBack triggered via: onModalHide={() => shouldGoBack.current && Navigation.goBack()}
    

    The old pattern ensured goBack() fired via onModalHide before deactivateCardAction ran (deferred by InteractionManager).


    Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, @MelvinBot <your feedback> to refine this analysis, or explain why you are rejecting Melvin's proposal.

  7. Pujan92 commented on May 16, 2026

    @Pujan92
    Contributor

    @mrejdak needs to fix this by navigating back first and then making a callback after the transition here.

    deactivateCardAction(defaultFundID, card);
    Navigation.goBack();

    Navigation.goBack();
    TransitionTracker.runAfterTransitions({
        callback: () => deactivateCardAction(defaultFundID, card),
        waitForUpcomingTransition: true
    })
  8. 3 remaining items

  9. added and removed
    DeployBlockerCashThis issue or pull request should block deployment
    on May 19, 2026
  10. melvin-bot commented on May 19, 2026

    @melvin-bot

    📣 @Ilya0527! 📣
    Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork.
    Please follow these steps:

    1. Make sure you've read and understood the contributing guidelines.
    2. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
    3. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
    4. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
      Screen Shot 2022-11-16 at 4 42 54 PM
      Format:
    Contributor details
    Your Expensify account email: <REPLACE EMAIL HERE>
    Upwork Profile Link: <REPLACE LINK HERE>
    
  11. changed the title [-]Expensify Card - Error page briefly shown when deactivate Expensify Card[/-] [+][Due for payment 2026-05-26] Expensify Card - Error page briefly shown when deactivate Expensify Card[/+] on May 19, 2026
  12. melvin-bot commented on May 19, 2026

    @melvin-bot

    Reviewing label has been removed, please complete the "BugZero Checklist".

  13. melvin-bot commented on May 19, 2026

    @melvin-bot

    The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.75-14 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

    If no regressions arise, payment will be issued on 2026-05-26. 🎊

    For reference, here are some details about the assignees on this issue:

    • @Pujan92 requires payment through NewDot Manual Requests
    • @collectioneur does not require payment (Contractor)
    • @mrejdak does not require payment (Contractor)
  14. melvin-bot commented on May 19, 2026

    @melvin-bot

    @Pujan92 The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]

  15. changed the title [-][Due for payment 2026-05-26] Expensify Card - Error page briefly shown when deactivate Expensify Card[/-] [+][Due for payment 2026-05-26] [Due for payment 2026-05-26] Expensify Card - Error page briefly shown when deactivate Expensify Card[/+] on May 19, 2026
  16. melvin-bot commented on May 19, 2026

    @melvin-bot

    The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.77-3 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

    If no regressions arise, payment will be issued on 2026-05-26. 🎊

    For reference, here are some details about the assignees on this issue:

    • @Pujan92 requires payment through NewDot Manual Requests
    • @collectioneur does not require payment (Contractor)
    • @mrejdak does not require payment (Contractor)
  17. melvin-bot commented on May 19, 2026

    @melvin-bot

    @Pujan92 The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]

  18. Pujan92 commented on May 20, 2026

    @Pujan92
    Contributor

    @roryabraham this issue can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2Engineering

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions