Skip to content

Duplicate corporate card transactions imported after Chase reconnect for the same card and same transaction details #84401

Description

@izarutskaya

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:
Reproducible in staging?: Needs Reproduction (Reproduction blocked)
Reproducible in production?: Needs Reproduction (Reproduction blocked)
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: @Leeckhart
Slack conversation (hyperlinked to channel name): #expensify_bugs

Action Performed:

  1. Corporate card (Chase via oauth.chase.com) became disconnected.
  2. Card was reconnected (exact disconnect timing unknown).
  3. After reconnect, historical transactions were re-imported.
  4. Identical transactions were imported more than once for the same card.
  5. Users identified and manually deleted some duplicates.

Expected Result:

Reconnecting a Chase card feed should not re-import identical historical transactions. Duplicate transactions with matching cardID, amount, date, and bank source should be automatically deduplicated.

Actual Result:

After reconnecting the Chase card, identical transactions were re-imported, creating duplicate expenses for the same card and transaction details. Users must manually identify and delete duplicates.

Workaround:

Unknown

Platforms:

Select the officially supported platforms where the issue was reproduced:

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

Screenshots/Videos

Check the OP for details

View all open jobs on GitHub

Issue OwnerCurrent Issue Owner: @grgia
Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~022029985448545427696
  • Upwork Job ID: 2029985448545427696
  • Last Price Increase: 2026-03-06

Activity

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

    @MelvinBot
    Contributor

    This has been labelled "Needs Reproduction". Follow the steps here: https://stackoverflowteams.com/c/expensify/questions/16989

  3. added
    BugSomething is broken. Auto assigns a BugZero manager.
    and removed
    BugSomething is broken. Auto assigns a BugZero manager.
    on Mar 6, 2026
  4. melvin-bot commented on Mar 6, 2026

    @melvin-bot

    Triggered auto assignment to @laurenreidexpensify (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

  5. MelvinBot commented on Mar 6, 2026

    @MelvinBot
    Contributor

    Proposal

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

    After a Chase corporate card feed (via oauth.chase.com) becomes disconnected and is reconnected, historical transactions are re-imported as duplicates. Identical transactions with matching cardID, amount, date, and bank source are not automatically deduplicated at import time, forcing users to manually identify and delete the duplicates.

    What is the root cause of that problem?

    The root cause is that the card feed reconnection flow does not pass the existing card's scrapeMinDate (transaction start date) to the backend, which likely causes the backend to re-import all available historical transactions rather than only fetching new ones.

    Specifically, getCompanyCardBankConnection() in App/src/libs/actions/getCompanyCardBankConnection/index.tsx:38 hardcodes scrapeMinDate: '' (empty string) when building the OAuth callback URL:

    const params: CompanyCardBankConnection = {
        authToken: authToken ?? '',
        isNewDot: 'true',
        domainName: PolicyUtils.getDomainNameForPolicy(policyID),
        isCorporate: 'true',
        scrapeMinDate: '',  // Always empty — ignores existing per-card value
    };

    This URL is used for both initial connections AND reconnections of broken feeds. When reconnecting, the function does not accept or forward the existing card's scrapeMinDate, so the backend's oauth_callback.php receives no date constraint.

    After re-authentication, the useUpdateFeedBrokenConnection hook (App/src/hooks/useUpdateFeedBrokenConnection.ts:23-28) calls updateWorkspaceCompanyCard() which fires the SYNC_CARD API command with only a cardID parameter — again with no scrapeMinDate or start date filter (App/src/libs/actions/CompanyCards.ts:583-592).

    In contrast, during initial card assignment, the user is explicitly asked to choose a transaction start date (either 90 days back or a custom date), which is properly sent to the backend via AssignCard. And the start date can be edited later via UpdateCardTransactionStartDate. But neither of these mechanisms is invoked during reconnection.

    Additionally, the App's duplicate detection is entirely backend-driven — the backend attaches DUPLICATED_TRANSACTION violations to transactions it considers duplicates, and the frontend only displays and resolves them. The App repo contains no client-side dedup logic that prevents duplicate imports.

    Confidence: Medium-High. The empty scrapeMinDate during reconnection is confirmed in code. However, confirming whether the backend falls back to the stored per-card scrapeMinDate or defaults to importing all history requires backend investigation (Auth/Web-Expensify repos), which are not available in this workspace.

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

    This likely needs a two-pronged approach:

    1. Frontend fix (App repo): Modify getCompanyCardBankConnection() to accept an optional scrapeMinDate parameter and pass it through to the OAuth callback URL. When called during the reconnection flow (from BankConnection component), look up the existing card's scrapeMinDate and pass it so the backend knows the minimum date constraint. The BankConnection component at App/src/pages/workspace/companyCards/BankConnection/index.tsx already has access to the feed and card data needed to retrieve this value.

    2. Backend fix (Auth/Web-Expensify): The backend's oauth_callback.php handler for Chase and the SyncCard command handler should be verified to ensure they properly look up and respect the stored per-card scrapeMinDate when the URL parameter is empty or when syncing after reconnection. The backend should also have proper deduplication logic (matching on bank-provided unique transaction identifiers) to prevent re-importing transactions that already exist, regardless of date range.

    What alternative solutions did you explore? (Optional)

    • Prompt user for start date during reconnection: Add a step to the broken connection fix flow that asks the user to confirm the transaction start date before reconnecting. This was considered but seems unnecessary if the existing scrapeMinDate can simply be forwarded automatically.
    • Client-side dedup: Have the frontend detect and filter duplicates before displaying. This is impractical since transaction import happens entirely on the backend, and the frontend's role should remain limited to displaying and resolving flagged duplicates.

    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.

  6. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Mar 6, 2026
  7. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Mar 6, 2026
  8. melvin-bot commented on Mar 6, 2026

    @melvin-bot

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

  9. melvin-bot commented on Mar 6, 2026

    @melvin-bot
  10. 21 remaining items

  11. melvin-bot commented on Mar 12, 2026

    @melvin-bot

    Triggered auto assignment to @grgia, see https://stackoverflow.com/c/expensify/questions/7972 for more details.

  12. changed the title [-][$250] Duplicate corporate card transactions imported after Chase reconnect for the same card and same transaction details[/-] [+]Duplicate corporate card transactions imported after Chase reconnect for the same card and same transaction details[/+] on Mar 12, 2026
  13. grgia commented on Mar 12, 2026

    @grgia
    Contributor

    Asked for QA retest to ensure this was not fixed in the card import related fires recently

  14. grgia commented on Mar 17, 2026

    @grgia
    Contributor
  15. melvin-bot commented on Mar 20, 2026

    @melvin-bot

    @grgia @laurenreidexpensify @DylanDylann this issue was created 2 weeks ago. Are we close to approving a proposal? If not, what's blocking us from getting this issue assigned? Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!

  16. melvin-bot commented on Mar 20, 2026

    @melvin-bot

    @grgia Whoops! This issue is 2 days overdue. Let's get this updated quick!

  17. melvin-bot commented on Mar 20, 2026

    @melvin-bot

    @grgia Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

  18. melvin-bot commented on Mar 24, 2026

    @melvin-bot

    @grgia Still overdue 6 days?! Let's take care of this!

  19. grgia commented on Mar 26, 2026

    @grgia
    Contributor

    closing out, let's reopen if it happens again

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.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorHelp WantedApply this label when an issue is open to proposals by contributorsInternalRequires API changes or must be handled by Expensify staffNeeds ReproductionReproducible steps needed

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions