{"record":{"id":"da0398f9fd901072","repo":"actualbudget/actual","slug":"account-with-id-accountid-does-not-exist","errorCode":null,"errorMessage":"Account with ID ${accountId} does not exist.","messagePattern":"Account with ID (.+?) does not exist\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/modals/modalsSlice.ts","lineNumber":705,"sourceCode":"\nexport const openAccountCloseModal = createAppAsyncThunk(\n  `${sliceName}/openAccountCloseModal`,\n  async ({ accountId }: OpenAccountCloseModalPayload, { dispatch, extra }) => {\n    const {\n      balance,\n      numTransactions,\n    }: { balance: number; numTransactions: number } = await send(\n      'account-properties',\n      {\n        id: accountId,\n      },\n    );\n    const queryClient = extra.queryClient;\n    const accounts = await queryClient.ensureQueryData(accountQueries.list());\n    const account = accounts.find(acct => acct.id === accountId);\n\n    if (!account) {\n      throw new Error(`Account with ID ${accountId} does not exist.`);\n    }\n\n    dispatch(\n      pushModal({\n        modal: {\n          name: 'close-account',\n          options: {\n            account,\n            balance,\n            canDelete: numTransactions === 0,\n          },\n        },\n      }),\n    );\n  },\n);\n\ntype ModalsState = {","sourceCodeStart":687,"sourceCodeEnd":723,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/modals/modalsSlice.ts#L687-L723","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Verify the accountId is current by refetching the accounts list before dispatching the modal action","Invalidate and refetch accountQueries.list() if accounts were recently modified","Guard the call site: only open the close-account modal from a freshly rendered account row","Handle the rejection in the caller (toast a friendly message) instead of letting it surface as an unhandled thunk rejection"],"exampleFix":"// before\nconst account = accounts.find(acct => acct.id === accountId);\nif (!account) {\n  throw new Error(`Account with ID ${accountId} does not exist.`);\n}\n// after\nconst account = accounts.find(acct => acct.id === accountId);\nif (!account) {\n  await queryClient.invalidateQueries({ queryKey: accountQueries.list().queryKey });\n  const fresh = await queryClient.fetchQuery(accountQueries.list());\n  if (!fresh.some(acct => acct.id === accountId)) {\n    throw new Error(`Account with ID ${accountId} does not exist.`);\n  }\n}","handlingStrategy":"validation","validationCode":"const accounts = await queryClient.ensureQueryData(accountQueries.list());\nconst exists = accounts.some(acct => acct.id === accountId);\nif (!exists) {\n  toast('This account no longer exists');\n  return;\n}\nopenCloseAccountModal(accountId);","typeGuard":"const findAccount = (accounts: AccountEntity[], id: string): AccountEntity | undefined =>\n  accounts.find(a => a.id === id);","tryCatchPattern":"try {\n  await dispatch(openCloseAccountModal(accountId)).unwrap();\n} catch (err) {\n  if (err.message.includes('does not exist')) {\n    toast('Account not found — refreshing list');\n    queryClient.invalidateQueries({ queryKey: accountQueries.list().queryKey });\n  } else throw err;\n}","preventionTips":["Invalidate the accounts query after any delete/sync so the UI list is never stale","Always derive accountId from freshly rendered data, not cached deep-link state","Handle thunk rejections with .unwrap() and user-facing toasts","When switching budget files, clear modal state holding ids from the previous file"],"tags":["state","react-query","not-found"],"backgroundTag":"resource-not-found","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}