actualbudget/actual · error
Transaction not found: ${id}
Error message
Transaction not found: ${id} What it means
moveTransaction first fetches the transaction by id; if getTransaction returns nothing (missing or tombstoned row), it throws 'Transaction not found: <id>'. This is a precondition check before the move is applied, inside a batchMessages transaction so no partial writes occur.
Source
Thrown at packages/loot-core/src/server/db/index.ts:922
}
/**
* Move a transaction to a new position within the same date.
* Uses the same midpoint/shove algorithm as category reordering.
*
* @param id - The ID of the transaction to move
* @param accountId - The account the transaction belongs to
* @param targetId - The ID of the transaction to place AFTER, or null to place at top
*/
export async function moveTransaction(
id: string,
accountId: string,
targetId: string | null,
) {
await batchMessages(async () => {
const transaction = await getTransaction(id);
if (!transaction) {
throw new Error(`Transaction not found: ${id}`);
}
// Validate that the transaction belongs to the specified account
if (transaction.account !== accountId) {
throw new Error(
`Transaction ${id} does not belong to account ${accountId}`,
);
}
// Convert date string (YYYY-MM-DD) to integer format (YYYYMMDD) for SQL query
const dateInt = parseInt(transaction.date.replace(/-/g, ''), 10);
// Get transactions to reorder against.
// If this is a child transaction, scope to siblings with the same parent_id.
// Otherwise, get all parent transactions for the same date (excluding children).
// Query in DESC order to match UI display order.
const isChild = transaction.is_child && transaction.parent_id;
const transactions = await all<{ id: string; sort_order: number }>(View on GitHub (pinned to d4334cb6e6)
Solutions
- Verify the transaction id exists via getTransactions/getTransaction before moving
- Refresh client state (re-sync) so stale ids are replaced with current ones
- If the transaction was deleted intentionally, drop the queued move operation instead of retrying
- Check that the correct id field is being passed (transaction id, not payee/schedule/category id)
Example fix
// before
await aqlQuery.moveTransaction(txnId, accountId, targetId);
// after
const txn = await aqlQuery.getTransaction(txnId);
if (txn) {
await aqlQuery.moveTransaction(txnId, accountId, targetId);
} Defensive patterns
Strategy: try-catch
Validate before calling
const txn = await aqlQuery.getTransaction(txnId);
if (!txn) throw new Error(`Cannot move: transaction ${txnId} not found`); Type guard
function isExistingTransaction(txn: unknown): txn is { id: string; account: string; date: string } {
return !!txn && typeof (txn as any).id === 'string' && typeof (txn as any).account === 'string';
} Try / catch
try {
await aqlQuery.moveTransaction(txnId, accountId, targetId);
} catch (e) {
if (e.message.startsWith('Transaction not found')) {
await resync(); // drop stale id from queue
} else throw e;
} Prevention
- Re-sync before replaying queued operations that reference transaction ids
- Never cache transaction ids across sessions without validation
- Verify id source: pass transaction ids, not other entity ids
- Treat deleted transactions as terminal — drop, don't retry, the operation
When it happens
Trigger: Calling moveTransaction with an id that never existed, a transaction deleted on this or another device (tombstoned), a stale id from cached client state, or a malformed/truncated id string.
Common situations: Multi-device sync where one device deleted the transaction while another tries to move it; replaying queued offline operations referencing old ids; passing a payee or category id by mistake instead of the transaction id.
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
- Schedule not found: ${id}
- Transaction not found: ${id}
- Tried to edit unknown transaction id: ${id}
- Sync ID is required for sync ${flag}. Set --sync-id or ACTUA
- Could not resolve on-disk budget id for syncId ${syncId} aft
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/4f4013bb81cd6151.
Report an issue: GitHub.