actualbudget/actual · error · BankSyncError
res.error_code
res.error_code
Error message
BankSyncError(res.error_type, res.error_code, errorDetails)
What it means
downloadGoCardlessTransactions posts to the GoCardless Bank Account Details proxy and, when the response carries an error_code, throws a BankSyncError wrapping the upstream error_type and error_code (plus rateLimitHeaders in details). This surfaces upstream GoCardless/Nordigen failures — expired requisitions, permission errors, rate limits — as structured bank-sync errors. The error_code is the raw upstream code, so its meaning depends on the provider response.
Source
Thrown at packages/loot-core/src/server/accounts/sync.ts:163
{
userId,
key: userKey,
requisitionId: bankId,
accountId: acctId,
startDate: since,
includeBalance,
},
{
'X-ACTUAL-TOKEN': userToken,
},
);
if (res.error_code) {
const errorDetails = {
rateLimitHeaders: res.rateLimitHeaders,
};
throw BankSyncError(res.error_type, res.error_code, errorDetails);
}
if (includeBalance) {
const {
transactions: { all },
balances,
startingBalance,
} = res;
logger.log('Response:', res);
return {
transactions: all,
accountBalance: balances,
startingBalance,
};
} else {
logger.log('Response:', res);View on GitHub (pinned to d4334cb6e6)
Solutions
- Inspect error_code/error_type on the thrown BankSyncError; if it is rate-limit related, wait and retry honoring rateLimitHeaders.
- If the requisition expired, re-run the GoCardless link/requisition flow to get fresh consent, then resync.
- Verify bankId (requisitionId) and acctId are current by listing accounts for the requisition via the sync server.
- Check sync-server logs and GoCardless status for upstream outages before retrying.
Defensive patterns
Strategy: try-catch
Validate before calling
// Verify the requisition is still valid before syncing:
const accounts = await bankSync.listGoCardlessAccounts(); // or via sync server API
if (!accounts.some(a => a.accountId === acctId)) {
throw new Error('GoCardless account/requisition no longer valid — re-link required');
} Type guard
function isBankSyncError(e: unknown): e is { errorType: string; errorCode: string; details?: { rateLimitHeaders?: unknown } } {
return typeof e === 'object' && e !== null && 'errorType' in e && 'errorCode' in e;
} Try / catch
try {
await syncAccount(accountId);
} catch (e) {
if (isBankSyncError(e)) {
if (e.errorCode === 'RE_RATE_LIMIT') {
const retryAfter = e.details?.rateLimitHeaders; // wait per headers then retry
} else {
// expired requisition / permissions: prompt user to re-link the bank
}
} else throw e;
} Prevention
- Re-authenticate bank links proactively before requisitions expire.
- Respect provider rate limits; avoid frequent back-to-back syncs.
- Catch BankSyncError specifically and branch on error_code rather than generic catches.
- Monitor GoCardless status and sync-server logs for upstream incidents.
When it happens
Trigger: Any GoCardless /transactions response containing error_code, e.g. an expired or cancelled requisition, an account whose access has lapsed, or HTTP 429 rate limiting (RE_RATE_LIMIT) captured via rateLimitHeaders.
Common situations: End-user's bank consent expired and must be re-authenticated; too many sync requests hitting provider rate limits; bankId/requisitionId referencing a deleted requisition; GoCardless account credentials revoked.
Related errors
- Account with ID ${upgradingId} not found.
- Requisition not linked yet
- Provided account id is not linked to given requisition
- ITEM_LOGIN_REQUIRED
- RATE_LIMIT_EXCEEDED
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/ad669c2eca8cec7e.
Report an issue: GitHub.