actualbudget/actual · error

Account with ID ${accountId} not found.

Error message

Account with ID ${accountId} not found.

What it means

onDoneReconciling in AccountInternal resolves the account from the `accounts` prop by accountId and throws if it's missing, before finishing reconciliation. Like the rename path, it treats a desync between accountId and the accounts list as an unrecoverable invariant violation.

Source

Thrown at packages/desktop-client/src/components/accounts/Account.tsx:1089

    await reconciliation.lockTransactions(accountId);
    await this.refetchTransactions();
  };

  onReconcile = async (amount: number | null) => {
    this.setState(({ showCleared }) => ({
      reconcileAmount: amount,
      showCleared: true,
      prevShowCleared: showCleared,
    }));
  };

  onDoneReconciling = async () => {
    const { accountId } = this.props;
    const account = this.props.accounts.find(
      account => account.id === accountId,
    );
    if (!account) {
      throw new Error(`Account with ID ${accountId} not found.`);
    }

    const { reconcileAmount } = this.state;

    await reconciliation.finishReconciliation(account.id, reconcileAmount, () =>
      this.lockTransactions(),
    );

    const lastReconciled = new Date().getTime().toString();
    this.props.onUpdateAccount({ ...account, last_reconciled: lastReconciled });

    this.setState(state => ({
      reconcileAmount: null,
      showCleared: state.prevShowCleared,
    }));
  };

  onCreateReconciliationTransaction = async (diff: number) => {

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Disable the reconciliation UI until the account resolves from the accounts list (guard render on account existence).
  2. Re-read the account from the persistent store by id rather than the prop so it survives list filtering.
  3. Handle the missing case gracefully: close the reconciliation modal, show a notice, and navigate away from the deleted account.

Example fix

// before
const account = this.props.accounts.find(a => a.id === accountId);
if (!account) throw new Error(`Account with ID ${accountId} not found.`);

// after
const account = this.props.accounts.find(a => a.id === accountId);
if (!account) {
  // account disappeared mid-reconciliation; abort quietly
  this.props.navigate('/accounts');
  return;
}
Defensive patterns

Strategy: validation

Validate before calling

const account = accounts.find(a => a.id === accountId);
if (!account) {
  navigate('/accounts'); // abort reconciliation for a vanished account
  return;
}

Type guard

function accountExists(accounts: AccountEntity[], accountId: string | undefined): accounts is AccountEntity[] & { length: number } {
  return accountId != null && accounts.some(a => a.id === accountId);
}

Try / catch

try {
  await onDoneReconciling();
} catch (e) {
  if (String(e.message).includes('not found')) {
    closeModal();
    notify('Account was deleted before reconciliation could finish.');
  } else throw e;
}

Prevention

When it happens

Trigger: Clicking 'Done reconciling' after the account was deleted (another tab/device/sync removal) or before the accounts list has loaded; accountId from the route no longer present in the prop.

Common situations: Multi-tab usage where one tab deletes the account mid-reconciliation; sync removing the account while the reconciler modal is open; slow initial load letting a user finish reconciliation first.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/af930d34afe7387f. Report an issue: GitHub.