actualbudget/actual · error

Account with ID ${accountId} does not exist.

Error message

Account with ID ${accountId} does not exist.

What it means

In a modal-opening thunk in modalsSlice.ts, the code fetches the accounts list via queryClient.ensureQueryData(accountQueries.list()) and searches for the requested accountId. If no account matches, it throws 'Account with ID <id> does not exist.' to abort opening the close-account modal for a missing account.

Source

Thrown at packages/desktop-client/src/modals/modalsSlice.ts:705

export const openAccountCloseModal = createAppAsyncThunk(
  `${sliceName}/openAccountCloseModal`,
  async ({ accountId }: OpenAccountCloseModalPayload, { dispatch, extra }) => {
    const {
      balance,
      numTransactions,
    }: { balance: number; numTransactions: number } = await send(
      'account-properties',
      {
        id: accountId,
      },
    );
    const queryClient = extra.queryClient;
    const accounts = await queryClient.ensureQueryData(accountQueries.list());
    const account = accounts.find(acct => acct.id === accountId);

    if (!account) {
      throw new Error(`Account with ID ${accountId} does not exist.`);
    }

    dispatch(
      pushModal({
        modal: {
          name: 'close-account',
          options: {
            account,
            balance,
            canDelete: numTransactions === 0,
          },
        },
      }),
    );
  },
);

type ModalsState = {

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Verify the accountId is current by refetching the accounts list before dispatching the modal action
  2. Invalidate and refetch accountQueries.list() if accounts were recently modified
  3. Guard the call site: only open the close-account modal from a freshly rendered account row
  4. Handle the rejection in the caller (toast a friendly message) instead of letting it surface as an unhandled thunk rejection

Example fix

// before
const account = accounts.find(acct => acct.id === accountId);
if (!account) {
  throw new Error(`Account with ID ${accountId} does not exist.`);
}
// after
const account = accounts.find(acct => acct.id === accountId);
if (!account) {
  await queryClient.invalidateQueries({ queryKey: accountQueries.list().queryKey });
  const fresh = await queryClient.fetchQuery(accountQueries.list());
  if (!fresh.some(acct => acct.id === accountId)) {
    throw new Error(`Account with ID ${accountId} does not exist.`);
  }
}
Defensive patterns

Strategy: validation

Validate before calling

const accounts = await queryClient.ensureQueryData(accountQueries.list());
const exists = accounts.some(acct => acct.id === accountId);
if (!exists) {
  toast('This account no longer exists');
  return;
}
openCloseAccountModal(accountId);

Type guard

const findAccount = (accounts: AccountEntity[], id: string): AccountEntity | undefined =>
  accounts.find(a => a.id === id);

Try / catch

try {
  await dispatch(openCloseAccountModal(accountId)).unwrap();
} catch (err) {
  if (err.message.includes('does not exist')) {
    toast('Account not found — refreshing list');
    queryClient.invalidateQueries({ queryKey: accountQueries.list().queryKey });
  } else throw err;
}

Prevention

When it happens

Trigger: Dispatching the close-account modal action with an accountId that was deleted, belongs to another budget file, was never synced, or whose list query returned before the account was created (stale cache).

Common situations: Stale React Query cache after deleting accounts elsewhere; an id passed from an outdated UI list; a typo or wrong id from deep links/scripts; switching budget files while a stale id is still referenced.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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