{"record":{"id":"b9419d39627669af","repo":"actualbudget/actual","slug":"tried-to-edit-unknown-transaction-id-id","errorCode":null,"errorMessage":"Tried to edit unknown transaction id: ${id}","messagePattern":"Tried to edit unknown transaction id: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/shared/transactions.ts","lineNumber":199,"sourceCode":"    ) as TransactionEntity[],\n  );\n}\n\nfunction replaceTransactions(\n  transactions: readonly TransactionEntity[],\n  id: string,\n  func: (transaction: TransactionEntity) => TransactionEntity | null,\n): {\n  data: TransactionEntity[];\n  newTransaction: TransactionEntity | null;\n  diff: ReturnType<typeof diffItems<TransactionEntity>>;\n} {\n  const idx = transactions.findIndex(t => t.id === id);\n  const trans = transactions[idx];\n  const transactionsCopy = [...transactions];\n\n  if (idx === -1) {\n    throw new Error('Tried to edit unknown transaction id: ' + id);\n  }\n\n  if (trans.is_parent || trans.is_child) {\n    const parentIndex = findParentIndex(transactions, idx);\n    if (parentIndex == null) {\n      logger.log('Cannot find parent index');\n      return {\n        data: [],\n        diff: { added: [], deleted: [], updated: [] },\n        newTransaction: null,\n      };\n    }\n\n    const split = getSplit(transactions, parentIndex);\n    let grouped = func(groupTransaction(split));\n    const newSplit = ungroupTransaction(grouped);\n\n    let diff: ReturnType<typeof diffItems<TransactionEntity>>;","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/shared/transactions.ts#L181-L217","documentation":"replaceTransactions edits a transaction inside an in-memory transaction list by id. If no transaction in the list matches the given id, it throws this Error rather than silently failing, since continuing would corrupt split/parent bookkeeping.","triggerScenarios":"Calling updateTransaction, deleteTransaction, addSplitTransaction, splitTransaction, or replaceTransactions directly with an id that is not in the passed array — typically a stale id after the transaction was deleted, or an id from a different budget/dataset.","commonSituations":"UI acting on a cached transaction list after a concurrent delete; batch scripts replaying old ids; passing a child id to a function expecting it in the supplied list that no longer contains it; syncing conflicts that removed the row locally.","solutions":["Verify the transaction still exists (fetch current transactions and check the id) before editing","Refresh your transaction list so the id comes from current data","Handle the error in a try/catch and skip/re-fetch for stale ids","If editing programmatically, use the API's returned ids rather than hardcoded ones"],"exampleFix":"// before\nawait updateTransaction({ id: staleId, amount: 500 });\n// after\nconst exists = (await aqlQuery('SELECT id FROM transactions WHERE id = ?', [staleId])).length > 0;\nif (exists) await updateTransaction({ id: staleId, amount: 500 });","handlingStrategy":"try-catch","validationCode":"function transactionExists(transactions, id) {\n  return transactions.some(t => t.id === id);\n}\nif (!transactionExists(currentTransactions, id)) {\n  await refetchTransactions(); // refresh before editing\n}","typeGuard":null,"tryCatchPattern":"try {\n  await updateTransaction({ id, ...patch });\n} catch (e) {\n  if (e.message.startsWith('Tried to edit unknown transaction id')) {\n    await refetchTransactions(); // stale list; reload and reapply if still relevant\n  } else throw e;\n}","preventionTips":["Always derive ids from a freshly fetched transaction list","Re-fetch after any delete before issuing further edits","In scripts, verify each id exists in the DB before batch updates"],"tags":["transactions","validation","stale-data"],"backgroundTag":"entity-not-found","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}