Skip to content

[Bug]: Enable Banking duplicates every transaction when the ASPSP repeats the continuation_key (Trade Republic) #8846

Description

@dhx101

What happened?

Client version: v26.8.1
Server version: v26.8.1
Provider: Enable Banking (production app)
Bank: Trade Republic (ES)

Every transaction imported from Trade Republic via Enable Banking is created exactly twice, on the very first import into an empty account. It is not a re-import issue: a brand new transaction is duplicated the first time it is ever seen.

Root cause looks like the pagination loop in the Enable Banking service. In getAllTransactions, the page is pushed into the result array before the repeated-continuation-key check:

do {
  const result = await enableBankingService.getTransactions(
    accountUid, dateFrom, dateTo, continuationKey, psuHeaders);
  allTransactions.push(...result.transactions);            // pushed first
  if (result.continuation_key && result.continuation_key === continuationKey) break;  // checked after
  continuationKey = result.continuation_key;
  iteration++;
} while (continuationKey && iteration < maxIterations);

If the ASPSP returns a continuation_key on the last page and then answers that key with the same page and the same key (which is what Trade Republic appears to do), the loop pushes the same transactions a second time and only then breaks. That yields exactly two copies — never three — which matches what we observe.

Trade Republic makes this visible because it returns transactions with no identifying fields at all: entry_reference, transaction_id, remittance information and creditor/debtor names are all empty. The server maps

const transactionId = tx.entry_reference || tx.transaction_id || "";

so imported_id ends up empty, dedup on import cannot fire, and fuzzy matching has nothing to match on either (no payee, no notes — only date and amount). Banks that return a usable entry_reference would silently swallow the second copy, which may be why this has gone unnoticed.

Evidence

Emptied the account completely, then ran a single bank sync:

amount imported_id imported_payee cleared
-10.00 "" "" true
-10.00 "" "" true
-201.00 "" "" true
250.00 "" "" true

The -10.00 is a transfer made minutes earlier and never touched in Actual — duplicated on its first ever import. Both copies are booked (cleared: true), so this is not the pending/booked overlap; unchecking Import pending transactions and Reimport deleted transactions makes no difference.

A second account at another bank (CaixaBank/imagin) on the same Enable Banking application imports correctly, with populated imported_id and payee, and is not paginated.

How can we reproduce the issue?

  1. Link a Trade Republic account with Enable Banking.
  2. Delete all transactions from the account so it is empty.
  3. Run Bank Sync once.
  4. Every transaction is present twice.

Suggested fix

Check the repeated continuation key before appending, e.g.

if (result.continuation_key && result.continuation_key === continuationKey) break;
allTransactions.push(...result.transactions);

or track seen continuation keys in a Set and stop when one repeats. A defensive de-duplication of the accumulated page contents would also help for ASPSPs that overlap pages.

Where are you hosting Actual?

Docker

What browsers are you seeing the problem on?

Firefox, Safari (iOS)

Operating System

Linux

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions