actualbudget/actual · error
Tried to edit unknown transaction id: ${id}
Error message
Tried to edit unknown transaction id: ${id} What it means
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.
Source
Thrown at packages/loot-core/src/shared/transactions.ts:199
) as TransactionEntity[],
);
}
function replaceTransactions(
transactions: readonly TransactionEntity[],
id: string,
func: (transaction: TransactionEntity) => TransactionEntity | null,
): {
data: TransactionEntity[];
newTransaction: TransactionEntity | null;
diff: ReturnType<typeof diffItems<TransactionEntity>>;
} {
const idx = transactions.findIndex(t => t.id === id);
const trans = transactions[idx];
const transactionsCopy = [...transactions];
if (idx === -1) {
throw new Error('Tried to edit unknown transaction id: ' + id);
}
if (trans.is_parent || trans.is_child) {
const parentIndex = findParentIndex(transactions, idx);
if (parentIndex == null) {
logger.log('Cannot find parent index');
return {
data: [],
diff: { added: [], deleted: [], updated: [] },
newTransaction: null,
};
}
const split = getSplit(transactions, parentIndex);
let grouped = func(groupTransaction(split));
const newSplit = ungroupTransaction(grouped);
let diff: ReturnType<typeof diffItems<TransactionEntity>>;View on GitHub (pinned to d4334cb6e6)
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
Example fix
// before
await updateTransaction({ id: staleId, amount: 500 });
// after
const exists = (await aqlQuery('SELECT id FROM transactions WHERE id = ?', [staleId])).length > 0;
if (exists) await updateTransaction({ id: staleId, amount: 500 }); Defensive patterns
Strategy: try-catch
Validate before calling
function transactionExists(transactions, id) {
return transactions.some(t => t.id === id);
}
if (!transactionExists(currentTransactions, id)) {
await refetchTransactions(); // refresh before editing
} Try / catch
try {
await updateTransaction({ id, ...patch });
} catch (e) {
if (e.message.startsWith('Tried to edit unknown transaction id')) {
await refetchTransactions(); // stale list; reload and reapply if still relevant
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- `date` is required when adding a transaction
- Amount is invalid, must be an integer: ${trans.amount}
- Subtransaction amount is invalid, must be an integer: ${sub.
- Transaction not found: ${id}
- Transaction ${id} does not belong to account ${accountId}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/b9419d39627669af.
Report an issue: GitHub.