Repository navigation
Description
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:
- Go to staging.new.expensify.com
- Create a Control workspace.
- Go to workspace settings > Company cards.
- Add Wells Fargo direct feed.
- After adding the direct feed, click on the feed dropdown.
- Click Add cards.
- Add the same Wells Fargo direct feed again.
- 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
Issue Owner
Current Issue Owner: @jjcoffeeUpwork Automation - Do Not Edit
- Upwork Job URL: https://www.upwork.com/jobs/~022043662402878158081
- Upwork Job ID: 2043662402878158081
- Last Price Increase: 2026-04-20
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
MelvinBot commentedon Apr 13, 2026
Proposal
What is the root cause of that problem?
When adding a duplicate direct feed (e.g., Wells Fargo via Plaid), the
BankConnectioncomponent shows a spinner and waits forisNewFeedConnectedto becometrue. This flag is computed bycheckIfNewFeedConnected, which checks whethercurrentFeeds.length > prevFeeds.lengthor 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 ofcardFeedsbygetCombinedCardFeedsFromAllFeeds— direct feeds withoutoAuthAccountDetailsand no assigned cards are excluded. This makesplaidBankFoundalsofalse.Additionally,
importPlaidAccountshas no error/failure handling forSHARED_NVP_PRIVATE_DOMAIN_MEMBER. If the backend rejects or ignores the duplicate, no Onyx updates are pushed,cardFeedsnever changes, and the spinner persists forever.What changes do you think we should make in order to solve the problem?
Two complementary fixes:
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
BankConnectionbefore callingimportPlaidAccounts.Add error handling in
BankConnectionfor the duplicate case. IfimportPlaidAccountscompletes butisNewFeedConnectedremainsfalseafter a reasonable timeout or after the API response returns, navigate the user back with an appropriate error message instead of spinning forever. TheimportPlaidAccountsaction should also include failure data forSHARED_NVP_PRIVATE_DOMAIN_MEMBERto handle backend rejections gracefully.What alternative solutions did you explore? (Optional)
isNewFeedConnectedbecometrue. However, this doesn't address the UX problem of allowing users to attempt duplicate additions in the first place.BankConnection/index.native.tsx) where a missingreturnafter theisNewFeedConnectedblock causesimportPlaidAccountsto fire even when navigation already happened. This should also be fixed.Investigation details
Key code locations:
StatementCloseDateStep.tsx:29-37— Submit handler transitions toBANK_CONNECTIONstep for Plaid flowsBankConnection/index.tsx:122-178— Web version: shows spinner, waits forisNewFeedConnected, callsimportPlaidAccountswhen falseBankConnection/index.native.tsx:141-158— Native version: missingreturnafterisNewFeedConnectedblockCardUtils.ts:980-994—checkIfNewFeedConnectedcompares prev/current feed countsCardFeedUtils.ts:629-635— Feed filtering removes direct feeds withoutoAuthAccountDetailsand no cardsPlaid.ts:122-163—importPlaidAccountshas no error handling forSHARED_NVP_PRIVATE_DOMAIN_MEMBERCardUtils.ts:1057-1077—getFeedTypereturns same key for duplicate direct feeds (no numbered suffix)CompanyCards.ts:220-238—addNewCompanyCardsFeedfailureData setscompanyCards[feedType]: null, which would destructively remove existing feed data for direct feed duplicatesData flow for the bug:
PlaidConnectionStepsetsaddNewCarddata and transitions toSELECT_STATEMENT_CLOSE_DATEStatementCloseDateStep→ transitions toBANK_CONNECTIONstepBankConnectionmounts, rendersActivityIndicator(spinner) for Plaid flowscheckIfNewFeedConnectedfinds: feed count unchanged (no new key) + existing feed filtered fromcardFeeds→isNewFeedConnected = falseimportPlaidAccountsfires but backend rejects/ignores duplicate → no Onyx updates → spinner foreverNext Steps for Contributor+ team: Reply with
@MelvinBot implement thisto create a draft PR,@MelvinBot <your feedback>to refine this analysis, or explain why you are rejecting Melvin's proposal.melvin-bot commentedon Apr 13, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @jjcoffee (
External)melvin-bot commentedon Apr 13, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022043662402878158081
[-]Company cards - RHP loads infinitely after adding existing direct feed[/-][+][$250] Company cards - RHP loads infinitely after adding existing direct feed[/+]wildan-m commentedon Apr 13, 2026
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
BankConnectionscreen relies oncheckIfNewFeedConnectedto recognize when the Plaid-connected feed has landed in Onyx and then step out of the flow. That helper is handed thecardFeedsvalue returned byuseCardFeeds, which is the display-filtered collection produced bygetCombinedCardFeedsFromAllFeeds.That filter intentionally strips any direct feed whose backend row has neither an
oAuthAccountDetailsentry nor a matching${domainID}_${feedName}record inWORKSPACE_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 populateoAuthAccountDetails, 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 ofcardFeeds, both branches ofcheckIfNewFeedConnectedmiss it:currentFeeds.length > prevFeeds.lengthstays false because the duplicateimportPlaidAccountscall is a no-op on the backend (the feed already exists) and no new row is ever added.plaidBankFoundbranch searches the same filteredcurrentFeedslist viasplitCardFeedWithDomainID, so it cannot see the hidden entry either.isNewFeedConnectedtherefore staysfalsefor the lifetime of the screen. The effect in BankConnection keeps re-dispatchingimportPlaidAccountseach 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
checkIfNewFeedConnectedlooks at. The display filter insidegetCombinedCardFeedsFromAllFeedsis load-bearing for the workspace Company Cards list and other screens that useuseCardFeeds, so we should not weaken it. Instead, givecheckIfNewFeedConnectedan additional optional argument that carries the raw, pre-filter list of feed names for the workspace (the keys ofsettings.companyCardson the workspace's entry inSHARED_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.tsxandBankConnection/index.native.tsx, destructure the third tuple element fromuseCardFeeds(policyID)— that is already the unfilteredCardFeedsentry for the workspace thatuseCardFeedspulls off Onyx keyed byworkspaceAccountID. Take the list of keys from itssettings.companyCardsmap as the raw feed-name list, and pass that list intocheckIfNewFeedConnectedalongside the existing arguments.With this change, as soon as
BankConnectionmounts for a duplicate add attempt, the Plaid-name branch ofcheckIfNewFeedConnectedsees the pre-existingplaid.<institutionId>key in the raw list,isNewFeedConnectedistrueon 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 redundantIMPORT_PLAID_ACCOUNTSAPI 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)
SelectBankStep/PlaidConnectionStepbefore opening Plaid, or by rejecting the duplicate in the Submit handler ofStatementCloseDateStep. 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.getCombinedCardFeedsFromAllFeedsso 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.61 remaining items
trjExpensify commentedon May 22, 2026
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
jjcoffee commentedon May 28, 2026
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: https://github.com/Expensify/App/pull/53122/changes#r3316537872
Link to the comment on the Issue: [HOLD for payment 2024-12-07] Amex direct feeds polish #53083 (comment)
[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: N/A
[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.
[BugZero Assignee] Create a GH issue for creating/updating the regression test once above steps have been agreed upon.
Link to issue: https://github.com/Expensify/Expensify/issues/644675
Regression Test Proposal
Test:
Do we agree 👍 or 👎
Zapier Logs
Run ID: 00040eee-76a5-ae57-8578-1038a1e2ffc4dylanexpensify commentedon May 28, 2026
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 👍
jjcoffee commentedon May 28, 2026
@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.
jjcoffee commentedon May 28, 2026
Deployed
2026-05-26, so should be due payment2026-06-02.MelvinBot commentedon May 28, 2026
There isn't a single PR that caused this — the duplicate-feed case was simply never handled. Two PRs combined to create the symptom:
PR #53122 (by
VickyStash) — IntroducedcheckIfNewFeedConnectedwith acurrentFeeds.length > prevFeeds.lengthcomparison, replacing the original per-feed-name check. This assumes adding a feed always increases the count, which fails for duplicates.PR #64794 (by
narefyev91) — Added theActivityIndicatorspinner for Plaid flows, making the infinite wait visible as a spinning indicator.The original
BankConnectioncomponent (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.
jjcoffee commentedon Jun 2, 2026
@mallenexpensify Automation failed here, this should be due payment today. Checklist is here. Thanks! 🙏
melvin-bot commentedon Jun 3, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)mallenexpensify commentedon Jun 3, 2026
Payment Summary
Contributor+: @jjcoffee due $250 via NewDot
^ Test case created. Thx
twisterdotcom commentedon Jun 3, 2026
$250 approved for @jjcoffee in Report ID: R00SoAu7VjNr.
Zapier Logs
Run ID: 00040eee-2174-ac5e-6d43-df315c723924SO: https://stackoverflowteams.com/c/expensify/questions/7582