Skip to content

[$250] Company cards - RHP loads infinitely after adding existing direct feed #81573

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.11-53
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/13176706/test-cases/41236862
Email or phone of affected tester (no customers): sidnjosdnjdosijsojns@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: Mac 26.1 / Chrome
App Component: Workspace Settings

Action Performed:

  1. Go to staging.new.expensify.com
  2. Create a Control workspace.
  3. Go to workspace settings > Company cards.
  4. Add Wells Fargo direct feed.
  5. After adding the direct feed, click on the feed dropdown.
  6. Click Add cards.
  7. Add the same Wells Fargo direct feed again.
  8. After returning from Plaid flow, click Submit on Statement close date RHP.

Expected Result:

RHP will close after adding the same feed and clicking Submit on Statement close date RHP.

Actual Result:

RHP loads infinitely after adding the same feed and clicking Submit on Statement close date RHP.

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

1.mp4

View all open jobs on GitHub

Issue OwnerCurrent Issue Owner: @jjcoffee
Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~022043662402878158081
  • Upwork Job ID: 2043662402878158081
  • Last Price Increase: 2026-04-20

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    and removed on Apr 13, 2026
  2. MelvinBot commented on Apr 13, 2026

    @MelvinBot
    Contributor

    Proposal

    What is the root cause of that problem?

    When adding a duplicate direct feed (e.g., Wells Fargo via Plaid), the BankConnection component shows a spinner and waits for isNewFeedConnected to become true. This flag is computed by checkIfNewFeedConnected, which checks whether currentFeeds.length > prevFeeds.length or a matching Plaid bank is found in the current feeds list.

    For a duplicate feed, the backend doesn't add a new entry to companyCards (the feed already exists), so the feed count never increases. Meanwhile, the existing feed can be filtered out of cardFeeds by getCombinedCardFeedsFromAllFeeds — direct feeds without oAuthAccountDetails and no assigned cards are excluded. This makes plaidBankFound also false.

    Additionally, importPlaidAccounts has no error/failure handling for SHARED_NVP_PRIVATE_DOMAIN_MEMBER. If the backend rejects or ignores the duplicate, no Onyx updates are pushed, cardFeeds never changes, and the spinner persists forever.

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

    Two complementary fixes:

    1. Prevent duplicate feed addition at the UI level. Before allowing the user to proceed with adding a direct feed, check if the same feed already exists on the workspace. If it does, show an error/warning instead of letting the user go through the full Plaid flow and ending up stuck. This check could be added in the feed selection step or in BankConnection before calling importPlaidAccounts.

    2. Add error handling in BankConnection for the duplicate case. If importPlaidAccounts completes but isNewFeedConnected remains false after a reasonable timeout or after the API response returns, navigate the user back with an appropriate error message instead of spinning forever. The importPlaidAccounts action should also include failure data for SHARED_NVP_PRIVATE_DOMAIN_MEMBER to handle backend rejections gracefully.

    What alternative solutions did you explore? (Optional)

    • Relying solely on backend deduplication: The backend could silently succeed and push Onyx updates even for duplicate feeds, which would make isNewFeedConnected become true. However, this doesn't address the UX problem of allowing users to attempt duplicate additions in the first place.
    • A secondary bug exists on native (BankConnection/index.native.tsx) where a missing return after the isNewFeedConnected block causes importPlaidAccounts to fire even when navigation already happened. This should also be fixed.
    Investigation details

    Key code locations:

    Data flow for the bug:

    1. User completes Plaid flow for duplicate feed → PlaidConnectionStep sets addNewCard data and transitions to SELECT_STATEMENT_CLOSE_DATE
    2. User clicks Submit on StatementCloseDateStep → transitions to BANK_CONNECTION step
    3. BankConnection mounts, renders ActivityIndicator (spinner) for Plaid flows
    4. checkIfNewFeedConnected finds: feed count unchanged (no new key) + existing feed filtered from cardFeeds → isNewFeedConnected = false
    5. importPlaidAccounts fires but backend rejects/ignores duplicate → no Onyx updates → spinner forever

    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 Apr 13, 2026
  4. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Apr 13, 2026
  5. melvin-bot commented on Apr 13, 2026

    @melvin-bot

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

  6. melvin-bot commented on Apr 13, 2026

    @melvin-bot
  7. changed the title [-]Company cards - RHP loads infinitely after adding existing direct feed[/-] [+][$250] Company cards - RHP loads infinitely after adding existing direct feed[/+] on Apr 13, 2026
  8. wildan-m commented on Apr 13, 2026

    @wildan-m
    Contributor

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    When a workspace admin tries to add the same Plaid direct feed (e.g., Wells Fargo) a second time after it is already connected to the workspace, pressing Submit on the Statement Close Date step leaves the Right-Hand Panel stuck on a full-screen spinner. The panel never closes and no error is shown, so the add-new-card flow is effectively frozen until the user manually dismisses the RHP.

    What is the root cause of that problem?

    The BankConnection screen relies on checkIfNewFeedConnected to recognize when the Plaid-connected feed has landed in Onyx and then step out of the flow. That helper is handed the cardFeeds value returned by useCardFeeds, which is the display-filtered collection produced by getCombinedCardFeedsFromAllFeeds.

    That filter intentionally strips any direct feed whose backend row has neither an oAuthAccountDetails entry nor a matching ${domainID}_${feedName} record in WORKSPACE_CARDS_LIST. A pre-existing Plaid direct feed fits this description exactly at the moment the duplicate add flow is running — Plaid direct feeds do not populate oAuthAccountDetails, and the previously-imported card rows for that feed are not always in the lightweight "feed keys with assigned cards" map that the filter consults (the user may never have opened the workspace cards list long enough to populate them, or the first-pass mount of BankConnection reads Onyx before the card rows settle). Because the existing feed is filtered out of cardFeeds, both branches of checkIfNewFeedConnected miss it:

    • currentFeeds.length > prevFeeds.length stays false because the duplicate importPlaidAccounts call is a no-op on the backend (the feed already exists) and no new row is ever added.
    • The plaidBankFound branch searches the same filtered currentFeeds list via splitCardFeedWithDomainID, so it cannot see the hidden entry either.

    isNewFeedConnected therefore stays false for the lifetime of the screen. The effect in BankConnection keeps re-dispatching importPlaidAccounts each render without ever navigating away, and the activity indicator spins forever.

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

    Fix the data source the Plaid-name branch of checkIfNewFeedConnected looks at. The display filter inside getCombinedCardFeedsFromAllFeeds is load-bearing for the workspace Company Cards list and other screens that use useCardFeeds, so we should not weaken it. Instead, give checkIfNewFeedConnected an additional optional argument that carries the raw, pre-filter list of feed names for the workspace (the keys of settings.companyCards on the workspace's entry in SHARED_NVP_PRIVATE_DOMAIN_MEMBER). The Plaid-name branch consults that raw list when it is provided, while the count-based branch (currentFeeds.length > prevFeeds.length) stays unchanged so the helper continues to ignore stale rows for everything else.

    In BankConnection/index.tsx and BankConnection/index.native.tsx, destructure the third tuple element from useCardFeeds(policyID) — that is already the unfiltered CardFeeds entry for the workspace that useCardFeeds pulls off Onyx keyed by workspaceAccountID. Take the list of keys from its settings.companyCards map as the raw feed-name list, and pass that list into checkIfNewFeedConnected alongside the existing arguments.

    With this change, as soon as BankConnection mounts for a duplicate add attempt, the Plaid-name branch of checkIfNewFeedConnected sees the pre-existing plaid.<institutionId> key in the raw list, isNewFeedConnected is true on the very first pass of the effect, the RHP closes, and the user is returned to the Workspace Company Cards page — matching the Expected Result in the issue. A redundant IMPORT_PLAID_ACCOUNTS API request is also never dispatched, so the backend does not see the duplicate write.

    Compare branch: https://github.com/Expensify/App/compare/main...wildan-m:App:wildan/81573-duplicate-plaid-feed-infinite-spinner?expand=1

    What alternative solutions did you explore? (Optional)

    • Block the duplicate earlier in the flow, either by checking existing feeds in SelectBankStep / PlaidConnectionStep before opening Plaid, or by rejecting the duplicate in the Submit handler of StatementCloseDateStep. This would avoid the stuck spinner entirely but duplicates presence-check logic across every entry point that can open a Plaid feed flow and would require a new error-surfacing surface. Fixing the detection at its sole consumer is a much smaller change with the same user-visible effect.
    • Introduce a timeout-based fallback inside BankConnection that gives up after a few seconds and surfaces an error. That would also paper over the underlying detection miss, add an awkward delay on every legitimate add, and leave the user worse off than the proposed silent-navigate-back behavior that the issue's Expected Result explicitly describes.
    • Relax the stale-feed filter inside getCombinedCardFeedsFromAllFeeds so that fresh direct feeds stay visible until their card rows arrive. This has a much broader blast radius — the filter is relied on by the workspace Company Cards list and several related screens to hide genuinely stale direct feeds, and weakening it would regress views that are unrelated to this bug.
  9. 61 remaining items

  10. trjExpensify commented on May 22, 2026

    @trjExpensify
    Contributor

    I see. We're going to send a Concierge message but it's not in place yet. I do worry that'll be ignored in a sea of onboarding messages. @dylanexpensify it would be good to measure the impact of this change. I.e

    • Did more people set up a statement period before or after the change, or is it flat.
  11. jjcoffee commented on May 28, 2026

    @jjcoffee
    Contributor

    BugZero Checklist:

    Regression Test Proposal

    Test:

    1. Create a Control workspace.
    2. Go to workspace settings > Company cards.
    3. Add a Wells Fargo direct feed.
    4. After adding the direct feed, click on the feed dropdown.
    5. Click Add cards.
    6. Add the same Wells Fargo direct feed again.
    7. After returning from Plaid flow, verify that a "Card feed already connected" modal displays.

    Do we agree 👍 or 👎

    Zapier Logs Run ID: 00040eee-76a5-ae57-8578-1038a1e2ffc4
  12. dylanexpensify commented on May 28, 2026

    @dylanexpensify
    Contributor

    I see. We're going to send a Concierge message but it's not in place yet. I do worry that'll be ignored in a sea of onboarding messages. @dylanexpensify it would be good to measure the impact of this change. I.e

    Did more people set up a statement period before or after the change, or is it flat.

    Sorry for delay in seeing this, yes, will keep an eye on this metric by creating a funnel and comparing it with previous periods before change was live 👍

  13. jjcoffee commented on May 28, 2026

    @jjcoffee
    Contributor

    @MelvinBot Is there a PR that specifically caused the issue with the RHP spinner loading infinitely? I'm struggling to find one that's the direct cause, so it's possible that it was always around and just never tested for before.

  14. jjcoffee commented on May 28, 2026

    @jjcoffee
    Contributor

    Deployed 2026-05-26, so should be due payment 2026-06-02.

  15. MelvinBot commented on May 28, 2026

    @MelvinBot
    Contributor

    There isn't a single PR that caused this — the duplicate-feed case was simply never handled. Two PRs combined to create the symptom:

    1. PR #53122 (by VickyStash) — Introduced checkIfNewFeedConnected with a currentFeeds.length > prevFeeds.length comparison, replacing the original per-feed-name check. This assumes adding a feed always increases the count, which fails for duplicates.

    2. PR #64794 (by narefyev91) — Added the ActivityIndicator spinner for Plaid flows, making the infinite wait visible as a spinning indicator.

    The original BankConnection component (PR #50716) checked for a specific feed name rather than comparing counts, so it wouldn't have had this exact issue. The redesign in #53122 introduced the flawed assumption without considering the duplicate-feed edge case.

    Bottom line: This wasn't a regression from a specific change — it was an unconsidered edge case in the original design of the feed-count comparison logic.

  16. jjcoffee commented on Jun 2, 2026

    @jjcoffee
    Contributor

    @mallenexpensify Automation failed here, this should be due payment today. Checklist is here. Thanks! 🙏

  17. added
    Awaiting PaymentAuto-added when associated PR is deployed to production
    and removed
    ReviewingHas a PR in review
    on Jun 3, 2026
  18. melvin-bot commented on Jun 3, 2026

    @melvin-bot

    Triggered auto assignment to @mallenexpensify (Awaiting Payment)

  19. mallenexpensify commented on Jun 3, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor+: @jjcoffee due $250 via NewDot

    ^ Test case created. Thx

  20. twisterdotcom commented on Jun 3, 2026

    @twisterdotcom
    Contributor

    $250 approved for @jjcoffee in Report ID: R00SoAu7VjNr.

    Zapier Logs Run ID: 00040eee-2174-ac5e-6d43-df315c723924

    SO: https://stackoverflowteams.com/c/expensify/questions/7582

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.ExternalAdded to denote the issue can be worked on by a contributorWeeklyKSv2

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions