Skip to content

[Due for payment 2026-06-03] [$250] Add BA - Bank account is still shown in Payments after tapping Start over offline #89895

Description

@unzig21

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.67.11
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: https://test-management.browserstack.com/projects/2219752/folder/13176694/test-cases/43147208
Email or phone of affected tester (no customers): N/A
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: iPhone 15 iOS 26.3.1
App Component: Workspace Settings

Action Performed:

  1. Sign in to ND
  2. Create a workspace
  3. Navigate to Workspace settings/ Workflows/ Add bank account
  4. Start connecting a bank account flow (Plaid 1111), exit the flow after entering first and last name
  5. Tap the pending bank account row, go force offline
  6. Tap "Start over" and confirm
  7. "Kill" and re-launch the app, open Workspace settings/Workflows
  8. Go online, tap the pending bank account

Expected Result:

Pending bank account disappears from Payments after selecting "Start over" offline, refreshing the app, and going online

Actual Result:

Pending bank account is still displayed in Payments after selecting "Start over" offline, refreshing the app, and going online. After opening it, "Continue setup" and "Start over" options are shown.

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Bug7146311_1778088071736.Start_over_offline.mp4

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~022052317083169401266
  • Upwork Job ID: 2052317083169401266
  • Last Price Increase: 2026-05-07
Issue OwnerCurrent Issue Owner: @eVoloshchak

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on May 7, 2026
  2. MelvinBot commented on May 7, 2026

    @MelvinBot
    Contributor

    Proposal

    What is the root cause of that problem?

    resetUSDBankAccount (resetUSDBankAccount.ts) clears REIMBURSEMENT_ACCOUNT and policy.achAccount optimistically, but never touches BANK_ACCOUNT_LIST. The WorkspaceWorkflowsPage determines whether to show a pending bank account by searching BANK_ACCOUNT_LIST for an entry whose policyID matches the workspace. Since that entry is never removed, the pending bank account row persists across app restarts while offline.

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

    Add BANK_ACCOUNT_LIST updates to resetUSDBankAccount, following the existing pattern in deletePaymentBankAccount:

    • optimisticData: Set pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE on the bank account entry in BANK_ACCOUNT_LIST
    • successData: Set the entry to null to remove it
    • failureData: Restore the original entry

    The same fix should be applied to resetNonUSDBankAccount which has the identical omission.

    Investigation details

    Data flow (why the bug occurs):

    1. User taps "Start over" → WorkspaceResetBankAccountModal calls resetUSDBankAccount()
    2. Optimistic update clears REIMBURSEMENT_ACCOUNT.achData and policy.achAccount, but BANK_ACCOUNT_LIST is untouched
    3. Offline, so API.write() queues RestartBankAccountSetup for later
    4. App kill + relaunch → Onyx restores from disk, including the still-present BANK_ACCOUNT_LIST entry
    5. WorkspaceWorkflowsPage line 260 finds the entry and shows the pending bank account row
    6. Even after going online and the queued API call succeeding, the client-side successData also omits BANK_ACCOUNT_LIST cleanup — the entry persists until a full server data refresh

    Contrast with working pattern: deletePaymentBankAccount in BankAccounts.ts:583-611 correctly manages BANK_ACCOUNT_LIST in all three data phases (optimistic/success/failure).


    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.

  3. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on May 7, 2026
  4. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on May 7, 2026
  5. mukhrr commented on May 7, 2026

    @mukhrr
    Contributor

    Proposal

    What is the root cause of that problem?

    When the user taps "Start over" on a pending bank account, resetUSDBankAccount only clears REIMBURSEMENT_ACCOUNT.achData and policy.achAccount in optimisticData but never touches the BANK_ACCOUNT_LIST[bankAccountID] entry — and neither does successData/failureData.

    optimisticData: [
    {
    onyxMethod: Onyx.METHOD.MERGE,
    key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
    value: {
    shouldShowResetModal: false,
    isLoading: true,
    pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
    achData: null,
    },
    },
    {
    onyxMethod: Onyx.METHOD.MERGE,
    key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
    value: {
    achAccount: null,
    },
    },
    ],
    successData: [
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.ONFIDO_TOKEN,
    value: '',
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.ONFIDO_APPLICANT_ID,
    value: '',
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.PLAID_DATA,
    value: CONST.PLAID.DEFAULT_DATA,
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.RAM_ONLY_PLAID_LINK_TOKEN,
    value: '',
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
    value: CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA,
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT,
    value: {
    [INPUT_IDS.BENEFICIAL_OWNER_INFO_STEP.OWNS_MORE_THAN_25_PERCENT]: false,
    [INPUT_IDS.BENEFICIAL_OWNER_INFO_STEP.HAS_OTHER_BENEFICIAL_OWNERS]: false,
    [INPUT_IDS.BENEFICIAL_OWNER_INFO_STEP.BENEFICIAL_OWNERS]: '',
    [INPUT_IDS.BANK_INFO_STEP.ACCOUNT_NUMBER]: '',
    [INPUT_IDS.BANK_INFO_STEP.ROUTING_NUMBER]: '',
    [INPUT_IDS.BANK_INFO_STEP.PLAID_ACCOUNT_ID]: '',
    [INPUT_IDS.BANK_INFO_STEP.PLAID_MASK]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_NAME]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.STREET]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.CITY]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.STATE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.ZIP_CODE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_PHONE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_WEBSITE]: undefined,
    [INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_TAX_ID]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_TYPE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_DATE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_STATE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.HAS_NO_CONNECTION_TO_CANNABIS]: false,
    [INPUT_IDS.PERSONAL_INFO_STEP.FIRST_NAME]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.LAST_NAME]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.STREET]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.CITY]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.STATE]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.ZIP_CODE]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.IS_ONFIDO_SETUP_COMPLETE]: false,
    [INPUT_IDS.PERSONAL_INFO_STEP.DOB]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.SSN_LAST_4]: '',
    [INPUT_IDS.COMPLETE_VERIFICATION.ACCEPT_TERMS_AND_CONDITIONS]: false,
    [INPUT_IDS.COMPLETE_VERIFICATION.CERTIFY_TRUE_INFORMATION]: false,
    [INPUT_IDS.COMPLETE_VERIFICATION.IS_AUTHORIZED_TO_USE_BANK_ACCOUNT]: false,
    [INPUT_IDS.BANK_INFO_STEP.IS_SAVINGS]: false,
    [INPUT_IDS.BANK_INFO_STEP.BANK_NAME]: '',
    [INPUT_IDS.BANK_INFO_STEP.PLAID_ACCESS_TOKEN]: '',
    [INPUT_IDS.BANK_INFO_STEP.SELECTED_PLAID_ACCOUNT_ID]: '',
    [INPUT_IDS.AMOUNT1]: '',
    [INPUT_IDS.AMOUNT2]: '',
    [INPUT_IDS.AMOUNT3]: '',
    },
    },
    ],
    failureData: [
    {
    onyxMethod: Onyx.METHOD.MERGE,
    key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
    value: {isLoading: false, pendingAction: null},
    },
    {
    onyxMethod: Onyx.METHOD.MERGE,
    key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
    value: {
    achAccount,
    },
    },
    ],
    };

    The Workflows page renders the pending row from BANK_ACCOUNT_LIST (filtered by policyID), not just from policy.achAccount:

    const isBankAccountFullySetup = policy?.achAccount && (policy?.achAccount.state === CONST.BANK_ACCOUNT.STATE.OPEN || policy?.achAccount.state === CONST.BANK_ACCOUNT.STATE.LOCKED);
    const bankAccountConnectedToWorkspace = Object.values(bankAccountList ?? {}).find((bankAccount) => bankAccount?.accountData?.additionalData?.policyID === policy?.id);
    const bankName = isBankAccountFullySetup ? (policy?.achAccount?.bankName ?? '') : (bankAccountConnectedToWorkspace?.accountData?.additionalData?.bankName ?? '');
    const addressName = isBankAccountFullySetup ? (policy?.achAccount?.addressName ?? '') : (bankAccountConnectedToWorkspace?.accountData?.addressName ?? '');
    const accountData = isBankAccountFullySetup ? policy?.achAccount : bankAccountConnectedToWorkspace?.accountData;
    const bankTitle = addressName.includes(CONST.MASKED_PAN_PREFIX) ? bankName : addressName;
    const bankAccountID = isBankAccountFullySetup ? policy?.achAccount?.bankAccountID : bankAccountConnectedToWorkspace?.methodID;
    const state = isBankAccountFullySetup ? (policy?.achAccount?.state ?? '') : (bankAccountConnectedToWorkspace?.accountData?.state ?? '');
    const isAccountInSetupState = isBankAccountPartiallySetup(state);
    const isBusinessBankAccountLocked = state === CONST.BANK_ACCOUNT.STATE.LOCKED;
    const shouldShowBankAccount = (!!isBankAccountFullySetup || !!bankAccountConnectedToWorkspace) && policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO;

    So the offline flow plays out like this:

    1. Offline "Start over" applies the optimistic update — achData/policy.achAccount go to null, but the bankAccountList[bankAccountID] entry is untouched. The RESTART_BANK_ACCOUNT_SETUP request is queued in PersistedRequests.
    2. The user kills and re‑launches the app while still offline. Onyx is re‑hydrated and policy.achAccount is null at this point, but the bankAccountList entry still exists.
    3. Going online flushes the queued request. The Pusher update that removes the bank account from bankAccountList can be missed in this offline → kill → online sequence (this is the same edge case closeAccount already guards against, see the comment at
      // Sometimes pusher updates aren't received when we close the App while still offline,
      // so we are setting the bankAccount to null here to ensure that it gets cleared out once we come back online.
      successData: [
      {
      onyxMethod: Onyx.METHOD.MERGE,
      key: `${ONYXKEYS.BANK_ACCOUNT_LIST}`,
      value: {[bankAccountID]: null},
      },
      ],
      ). With no client‑side cleanup of bankAccountList, the row remains, and because OpenWorkspace re‑hydrates policy.achAccount from the BE before/while the queued request is processed, "Continue setup / Start over" comes back too.

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

    I'd add a successData write that nulls the bankAccountList[bankAccountID] entry, matching the pattern already used by closePaymentBankAccount. That guarantees the row is cleaned up on the client even if the Pusher update is dropped due to the offline → kill → online sequence.

    In src/libs/actions/ReimbursementAccount/resetUSDBankAccount.ts, extend successData:

             successData: [
    +            {
    +                // Sometimes pusher updates aren't received when we close the App while still offline,
    +                // so we are setting the bankAccount to null here to ensure that it gets cleared out once we come back online.
    +                onyxMethod: Onyx.METHOD.MERGE,
    +                key: ONYXKEYS.BANK_ACCOUNT_LIST,
    +                value: {[bankAccountID]: null},
    +            },
                 {
                     onyxMethod: Onyx.METHOD.SET,
                     key: ONYXKEYS.ONFIDO_TOKEN,
                     value: '',
                 },

    To keep the offline UX consistent (the row should disappear immediately after confirming "Start over"), I'd also mark the entry as pendingAction: DELETE optimistically and restore it on failure:

             optimisticData: [
    +            {
    +                onyxMethod: Onyx.METHOD.MERGE,
    +                key: ONYXKEYS.BANK_ACCOUNT_LIST,
    +                value: {[bankAccountID]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}},
    +            },
                 {
                     onyxMethod: Onyx.METHOD.MERGE,
                     key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,

    Supporting changes:

    • Apply the same three BANK_ACCOUNT_LIST updates (optimistic pendingAction: DELETE, success null, failure {pendingAction: null, errors: ...}) in src/libs/actions/ReimbursementAccount/resetNonUSDBankAccount.ts, which has the identical gap for non‑USD workspaces.
    • The Workflows page already hides rows whose bankAccountConnectedToWorkspace resolves to a deleted entry, so no UI changes are needed.

    What alternative solutions did you explore? (Optional)

    • Clearing BANK_ACCOUNT_LIST[bankAccountID] directly in optimisticData (instead of marking pendingAction: DELETE then nulling on success). This works but loses the ability to restore the row on a real BE failure, and breaks the offline pending‑state UX that other delete flows already use.
    • Re‑fetching OpenWorkspace after going online to refresh policy.achAccount. This doesn't address the dropped‑Pusher case for bankAccountList and adds an extra round trip — the localized successData cleanup is cheaper and self‑contained.
  6. yusufdeveloper2903 commented on May 7, 2026

    @yusufdeveloper2903
    Contributor

    Proposal

    What is the root cause of that problem?

    The Workflows screen reads the pending bank account from two Onyx keys: policy.achAccount (when the BA is fully verified) and bankAccountList (when it is still in setup). The relevant lookup is here — bankAccountConnectedToWorkspace is whatever entry in BANK_ACCOUNT_LIST has additionalData.policyID === policy.id:

    const isBankAccountFullySetup = policy?.achAccount && (policy?.achAccount.state === CONST.BANK_ACCOUNT.STATE.OPEN || policy?.achAccount.state === CONST.BANK_ACCOUNT.STATE.LOCKED);
    const bankAccountConnectedToWorkspace = Object.values(bankAccountList ?? {}).find((bankAccount) => bankAccount?.accountData?.additionalData?.policyID === policy?.id);
    const bankName = isBankAccountFullySetup ? (policy?.achAccount?.bankName ?? '') : (bankAccountConnectedToWorkspace?.accountData?.additionalData?.bankName ?? '');
    const addressName = isBankAccountFullySetup ? (policy?.achAccount?.addressName ?? '') : (bankAccountConnectedToWorkspace?.accountData?.addressName ?? '');
    const accountData = isBankAccountFullySetup ? policy?.achAccount : bankAccountConnectedToWorkspace?.accountData;
    const bankTitle = addressName.includes(CONST.MASKED_PAN_PREFIX) ? bankName : addressName;
    const bankAccountID = isBankAccountFullySetup ? policy?.achAccount?.bankAccountID : bankAccountConnectedToWorkspace?.methodID;
    const state = isBankAccountFullySetup ? (policy?.achAccount?.state ?? '') : (bankAccountConnectedToWorkspace?.accountData?.state ?? '');
    const isAccountInSetupState = isBankAccountPartiallySetup(state);
    const isBusinessBankAccountLocked = state === CONST.BANK_ACCOUNT.STATE.LOCKED;
    const shouldShowBankAccount = (!!isBankAccountFullySetup || !!bankAccountConnectedToWorkspace) && policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO;

    When the user taps Start over in the BA setup, resetUSDBankAccount only clears REIMBURSEMENT_ACCOUNT and policy.achAccount; it never touches BANK_ACCOUNT_LIST:

    optimisticData: [
    {
    onyxMethod: Onyx.METHOD.MERGE,
    key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
    value: {
    shouldShowResetModal: false,
    isLoading: true,
    pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
    achData: null,
    },
    },
    {
    onyxMethod: Onyx.METHOD.MERGE,
    key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
    value: {
    achAccount: null,
    },
    },
    ],
    successData: [
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.ONFIDO_TOKEN,
    value: '',
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.ONFIDO_APPLICANT_ID,
    value: '',
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.PLAID_DATA,
    value: CONST.PLAID.DEFAULT_DATA,
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.RAM_ONLY_PLAID_LINK_TOKEN,
    value: '',
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
    value: CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA,
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT,
    value: {
    [INPUT_IDS.BENEFICIAL_OWNER_INFO_STEP.OWNS_MORE_THAN_25_PERCENT]: false,
    [INPUT_IDS.BENEFICIAL_OWNER_INFO_STEP.HAS_OTHER_BENEFICIAL_OWNERS]: false,
    [INPUT_IDS.BENEFICIAL_OWNER_INFO_STEP.BENEFICIAL_OWNERS]: '',
    [INPUT_IDS.BANK_INFO_STEP.ACCOUNT_NUMBER]: '',
    [INPUT_IDS.BANK_INFO_STEP.ROUTING_NUMBER]: '',
    [INPUT_IDS.BANK_INFO_STEP.PLAID_ACCOUNT_ID]: '',
    [INPUT_IDS.BANK_INFO_STEP.PLAID_MASK]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_NAME]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.STREET]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.CITY]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.STATE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.ZIP_CODE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_PHONE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_WEBSITE]: undefined,
    [INPUT_IDS.BUSINESS_INFO_STEP.COMPANY_TAX_ID]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_TYPE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_DATE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.INCORPORATION_STATE]: '',
    [INPUT_IDS.BUSINESS_INFO_STEP.HAS_NO_CONNECTION_TO_CANNABIS]: false,
    [INPUT_IDS.PERSONAL_INFO_STEP.FIRST_NAME]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.LAST_NAME]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.STREET]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.CITY]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.STATE]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.ZIP_CODE]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.IS_ONFIDO_SETUP_COMPLETE]: false,
    [INPUT_IDS.PERSONAL_INFO_STEP.DOB]: '',
    [INPUT_IDS.PERSONAL_INFO_STEP.SSN_LAST_4]: '',
    [INPUT_IDS.COMPLETE_VERIFICATION.ACCEPT_TERMS_AND_CONDITIONS]: false,
    [INPUT_IDS.COMPLETE_VERIFICATION.CERTIFY_TRUE_INFORMATION]: false,
    [INPUT_IDS.COMPLETE_VERIFICATION.IS_AUTHORIZED_TO_USE_BANK_ACCOUNT]: false,
    [INPUT_IDS.BANK_INFO_STEP.IS_SAVINGS]: false,
    [INPUT_IDS.BANK_INFO_STEP.BANK_NAME]: '',
    [INPUT_IDS.BANK_INFO_STEP.PLAID_ACCESS_TOKEN]: '',
    [INPUT_IDS.BANK_INFO_STEP.SELECTED_PLAID_ACCOUNT_ID]: '',
    [INPUT_IDS.AMOUNT1]: '',
    [INPUT_IDS.AMOUNT2]: '',
    [INPUT_IDS.AMOUNT3]: '',
    },
    },
    ],

    Online, this normally works because the backend deletes the entry and Pusher pushes a fresh BANK_ACCOUNT_LIST to the client. But once you go through offline → Start over → kill app → relaunch → online, the Pusher event is missed in the offline window (the app already documents this race in deletePaymentBankAccount: "Sometimes pusher updates aren't received when we close the App while still offline, so we are setting the bankAccount to null here to ensure that it gets cleared out once we come back online" —

    // Sometimes pusher updates aren't received when we close the App while still offline,
    // so we are setting the bankAccount to null here to ensure that it gets cleared out once we come back online.
    successData: [
    {
    onyxMethod: Onyx.METHOD.MERGE,
    key: `${ONYXKEYS.BANK_ACCOUNT_LIST}`,
    value: {[bankAccountID]: null},
    },
    ],
    ). So the queued RESTART_BANK_ACCOUNT_SETUP succeeds, but the stale BANK_ACCOUNT_LIST[bankAccountID] survives, bankAccountConnectedToWorkspace keeps resolving truthy, and the row keeps rendering with Continue setup / Start over.

    resetNonUSDBankAccount has the same omission:

    optimisticData: [
    {
    onyxMethod: Onyx.METHOD.MERGE,
    key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
    value: {
    shouldShowResetModal: false,
    isLoading: true,
    pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
    achData: null,
    },
    },
    ],
    successData: [
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT,
    value: null,
    },
    {
    onyxMethod: Onyx.METHOD.SET,
    key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
    value: CONST.REIMBURSEMENT_ACCOUNT.DEFAULT_DATA,
    },
    ],
    failureData: [
    {
    onyxMethod: Onyx.METHOD.MERGE,
    key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
    value: {isLoading: false, pendingAction: null},
    },
    ],
    };

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

    I'd mirror the pattern from deletePaymentBankAccount in both reset flows: optimistically mark the BANK_ACCOUNT_LIST entry for delete (so the Workflows row hides immediately offline), null it out on success (so the local list is correct even if Pusher is lost), and restore it on failure. Since bankAccount.methodID === policy.achAccount.bankAccountID (confirmed in SettlementButton/index.tsx:631), the same bankAccountID we already have keys into BANK_ACCOUNT_LIST.

    In src/libs/actions/ReimbursementAccount/resetUSDBankAccount.ts:

         optimisticData: [
             {
                 onyxMethod: Onyx.METHOD.MERGE,
                 key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
                 value: {
                     shouldShowResetModal: false,
                     isLoading: true,
                     pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
                     achData: null,
                 },
             },
             {
                 onyxMethod: Onyx.METHOD.MERGE,
                 key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
                 value: {
                     achAccount: null,
                 },
             },
    +        {
    +            onyxMethod: Onyx.METHOD.MERGE,
    +            key: ONYXKEYS.BANK_ACCOUNT_LIST,
    +            value: {[bankAccountID]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}},
    +        },
         ],
         successData: [
             // ...existing resets for ONFIDO_TOKEN, PLAID_DATA, REIMBURSEMENT_ACCOUNT, FORM_DRAFT, ...
    +        // Sometimes pusher updates aren't received when we close the App while still offline,
    +        // so we set the bankAccount to null here to ensure it's cleared out once we come back online.
    +        {
    +            onyxMethod: Onyx.METHOD.MERGE,
    +            key: ONYXKEYS.BANK_ACCOUNT_LIST,
    +            value: {[bankAccountID]: null},
    +        },
         ],

    failureData should add a matching {[bankAccountID]: {pendingAction: null}} so the row reappears cleanly if the request errors. resetNonUSDBankAccount.ts needs the same three additions inside the if (bankAccountID) branch (the early-return branch has no bankAccountID and no list entry to clean up, so it's untouched).

    After this, the Workflows screen and any other consumer of bankAccountList agree with policy.achAccount immediately, regardless of whether the Pusher push lands.

    What alternative solutions did you explore? (Optional)

    • Filter bankAccountList against policy.achAccount in the Workflows selector — i.e. don't show bankAccountConnectedToWorkspace if policy.achAccount was just nulled. Rejected: it papers over an inconsistent local store and every other reader of BANK_ACCOUNT_LIST (PaymentMethodList, Wallet, SettlementButton) would still see the ghost entry until the next refresh.
    • Re-fetch OpenReimbursementAccountPage on reconnect to repopulate BANK_ACCOUNT_LIST from the server. Rejected: still leaves a visible window where the row is shown with Continue setup, and adds a network round-trip for something the client already knows is gone.
    • Clear the entry by scanning bankAccountList for additionalData.policyID === policyID instead of using bankAccountID. Rejected: extra lookup with no benefit since methodID === bankAccountID is already the contract used in SettlementButton.
  7. melvin-bot commented on May 7, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @eVoloshchak (External)

  8. changed the title [-]Add BA - Bank account is still shown in Payments after tapping Start over offline[/-] [+][$250] Add BA - Bank account is still shown in Payments after tapping Start over offline[/+] on May 7, 2026
  9. melvin-bot commented on May 7, 2026

    @melvin-bot
  10. github-actions commented on May 7, 2026

    @github-actions
    Contributor

    ⚠️ @mukhrr Thanks for your proposal. Please update it to follow the proposal template, as proposals are only reviewed if they follow that format (note the mandatory sections).

  11. added
    InternalRequires API changes or must be handled by Expensify staff
    and removed
    ExternalAdded to denote the issue can be worked on by a contributor
    on May 7, 2026
  12. 29 remaining items

  13. melvin-bot commented on May 27, 2026

    @melvin-bot

    @eVoloshchak 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]

  14. melvin-bot commented on Jun 1, 2026

    @melvin-bot

    @eVoloshchak Huh... This is 4 days overdue. Who can take care of this?

  15. melvin-bot commented on Jun 2, 2026

    @melvin-bot

    Triggered auto assignment to @mallenexpensify (Awaiting Payment)

  16. melvin-bot commented on Jun 2, 2026

    @melvin-bot

    Payment Summary

    Upwork Job

    BugZero Checklist (@mallenexpensify)

    • I have verified the correct assignees and roles are listed above and updated the necessary manual offers
    • I have verified that there are no duplicate or incorrect contracts on Upwork for this job (https://www.upwork.com/ab/applicants/2052317083169401266/hired)
    • I have verified the PR was not reverted
    • I have applied any discounts due to bugs/regressions introduced by this PR
    • I have paid out the Upwork contracts or cancelled the ones that are incorrect
    • I have verified the payment summary above is correct
  17. mallenexpensify commented on Jun 3, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor+: @eVoloshchak due $250 via NewDot

    @eVoloshchak plz complete the BZ checklist and tag me in a post once you have. Thx

  18. eVoloshchak commented on Jun 8, 2026

    @eVoloshchak
    Contributor

    BugZero Checklist:

    • [Contributor] The offending PR and associated issue have been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.

      Link to the comment on the PR: N/A, this was not implemented initially
      Link to the comment on the Issue:

    • [Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.

      Link to discussion: regression wasn't critical

    • [Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.

    Regression Test Proposal

    Test:

    1. Sign in to ND
    2. Create a workspace
    3. Navigate to Workspace settings > Workflows > Add bank account
    4. Start connecting a bank account flow (Plaid 1111, Alberta Charleson), exit the flow after entering the first and last name
    5. Tap the pending bank account row
    6. Tap "Start over" and confirm
    7. Verify the pending bank account item is removed from Workflows
    8. Verify that no errors appear in the JS console

    Do we agree 👍 or 👎

    Zapier Logs Run ID: 00040eee-3bd3-a66e-fb97-601635d199a4
  19. eVoloshchak commented on Jun 8, 2026

    @eVoloshchak
    Contributor

    @mallenexpensify, checklist completed. Thx!

  20. added and removed on Jun 8, 2026
  21. JmillsExpensify commented on Aug 3, 2026

    @JmillsExpensify
    Contributor

    $250 approved for @eVoloshchak

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

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionBugSomething is broken. Auto assigns a BugZero manager.DailyKSv2InternalRequires API changes or must be handled by Expensify staff

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions