actualbudget / actualbudget/actual
[Bug]: Enable Banking duplicates every transaction when the ASPSP repeats the continuation_key (Trade Republic)
- Vorherrschende Sprache
- TypeScript
- Sterne
- 28.7k
- Forks
- 3k
- Ø Merge
- 2 T. 11 Std.
- Gemergte PRs (30 T.)
- 65
Beschreibung
### 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:
```js
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
```js
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.
```js
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
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.