{"record":{"id":"a02f8d1bed76f951","repo":"actualbudget/actual","slug":"transaction-not-found-id-a02f8d","errorCode":null,"errorMessage":"Transaction not found: ${id}","messagePattern":"Transaction not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/transactions/app.ts","lineNumber":81,"sourceCode":"\nasync function deleteTransaction(transaction: Pick<TransactionEntity, 'id'>) {\n  await handleBatchUpdateTransactions({ deleted: [transaction] });\n  return {};\n}\n\nasync function moveTransaction({\n  id,\n  accountId,\n  targetId,\n}: {\n  id: string;\n  accountId: string;\n  targetId: string | null;\n}) {\n  // Fetch the transaction to validate it exists and verify account\n  const transaction = await db.getTransaction(id);\n  if (!transaction) {\n    throw new Error(`Transaction not found: ${id}`);\n  }\n\n  // Validate that the provided accountId matches the transaction's actual account\n  // This prevents sort order calculations against the wrong account\n  if (transaction.account !== accountId) {\n    throw new Error(\n      `Account mismatch: transaction belongs to account ${transaction.account}, not ${accountId}`,\n    );\n  }\n\n  // Child transactions can be reordered within their parent's children\n  // The db.moveTransaction handles the sibling-scoped reordering for children\n\n  await db.moveTransaction(id, accountId, targetId);\n  return {};\n}\n\nasync function parseTransactionsFile({","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/transactions/app.ts#L63-L99","documentation":"moveTransaction() first fetches the transaction by id to validate it exists and belongs to the given account; if db.getTransaction(id) returns nothing it throws Error(`Transaction not found: ${id}`). This guards the sort-order recalculation from operating on a missing row.","triggerScenarios":"moveTransaction({ id, accountId, targetId }) is called with an id that doesn't exist (deleted transaction, stale list, wrong id string).","commonSituations":"UI keeping a reference to a transaction deleted on another device; plugin/API automation using ids from an old export; id typos in scripts.","solutions":["Re-fetch the transaction list and verify the id exists before moving","Refresh/sync so local data is current if the transaction was just created elsewhere","Handle the not-found case in the caller (skip or re-select a valid transaction)"],"exampleFix":"// before\nawait moveTransaction({ id, accountId, targetId }); // Error: Transaction not found\n// after\nconst tx = await getTransaction(id);\nif (!tx) throw new Error(`Transaction not found: ${id}`);\nawait moveTransaction({ id, accountId, targetId });","handlingStrategy":"validation","validationCode":"const tx = await getTransaction(id);\nif (!tx) {\n  throw new Error(`Cannot move: transaction ${id} not found`);\n}\nif (tx.account !== accountId) {\n  throw new Error('Transaction belongs to a different account');\n}","typeGuard":"function isTransactionNotFound(e: unknown, id: string): boolean {\n  return e instanceof Error && e.message === `Transaction not found: ${id}`;\n}","tryCatchPattern":"try {\n  await moveTransaction({ id, accountId, targetId });\n} catch (e) {\n  if (isTransactionNotFound(e, id)) {\n    await refreshTransactionList();\n  } else throw e;\n}","preventionTips":["Validate ids against a fresh transaction fetch before moving","Sync before acting on transactions created on other devices","Validate account ownership before invoking moves","Guard automation scripts against stale id lists"],"tags":["transactions","not-found"],"backgroundTag":"resource-not-found","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}