actualbudget/actual · error
Bank with ID ${bankId} not found.
Error message
Bank with ID ${bankId} not found. What it means
Actual looks up the bank (GoCardless/Nordigen institution link) row by its ID before performing a bank-side operation such as creating a requisition. If no row in the `banks` table matches the given bankId, the app cannot proceed and throws this error. It means the caller passed a bank ID that does not exist in this budget's database.
Source
Thrown at packages/loot-core/src/server/accounts/app.ts:1741
'SELECT COUNT(*) as count FROM accounts WHERE bank = ?',
[bankId],
);
// No more accounts are associated with this bank. We can remove
// it from GoCardless.
const userToken = await asyncStorage.getItem('user-token');
if (!userToken) {
return 'ok';
}
if (!accountWithBankResult || accountWithBankResult.count === 0) {
const bank = await db.first<Pick<db.DbBank, 'bank_id'>>(
'SELECT bank_id FROM banks WHERE id = ?',
[bankId],
);
if (!bank) {
throw new Error(`Bank with ID ${bankId} not found.`);
}
const serverConfig = getServer();
if (!serverConfig) {
throw new Error('Failed to get server config.');
}
const requisitionId = bank.bank_id;
try {
await post(
serverConfig.GOCARDLESS_SERVER + '/remove-account',
{
requisitionId,
},
{
'X-ACTUAL-TOKEN': userToken,
},View on GitHub (pinned to d4334cb6e6)
Solutions
- Verify the bankId exists for this budget (SELECT id FROM banks) or re-list linked banks in the UI; use a current ID.
- Re-link the bank in Settings > Bank sync to create a fresh bank row, then retry.
- If migrating budgets, re-authenticate the bank in the new budget instead of copying IDs across.
- Remove or update stale client caches/references to deleted bank IDs.
Example fix
// before
await crinx.getOrCreateRequisition('9f3c-...deleted-bank-id...');
// after
const banks = await listUserBanks(); // confirm a valid id first
await crinx.getOrCreateRequisition(banks[0].bank_id); Defensive patterns
Strategy: validation
Validate before calling
const bank = await db.first('SELECT bank_id FROM banks WHERE id = ?', [bankId]);
if (!bank) throw new Error(`Skipping: bank ${bankId} does not exist in this budget`); Type guard
function isBank(v: { bank_id: string } | null | undefined): v is { bank_id: string } {
return v != null && typeof v.bank_id === 'string';
} Try / catch
try {
await syncBank(bankId);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Bank with ID')) {
// bank no longer exists: re-link the bank before retrying
return;
}
throw e;
} Prevention
- Always fetch the current bank list from the budget before referencing a bankId.
- Never copy bank IDs between budgets or environments.
- Re-link banks after restoring/migrating a budget file instead of reusing old IDs.
- Check the banks table when debugging stale references.
When it happens
Trigger: Calling the bank-sync internals (via API or a stale UI client) with a bankId that was deleted, belongs to a different budget file, or was never created. Also happens with a migrated/restored budget where the `banks` row was removed but old references remain.
Common situations: Restoring an old budget file after re-linking the bank; switching sync-server instances or GoCardless accounts; using the API with an ID copied from another budget; the bank having been unlinked previously.
Related errors
- response.reason || response.error_code
- response.reason || response.error || fallbackMessage
- Account with ID ${upgradingId} not found.
- Transfer payee with account ID ${transferAccountId} not foun
- `payeeName` is required when adding a transaction
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/ad25cbe83200fb4f.
Report an issue: GitHub.