actualbudget/actual · error
findSort: item not found: ${targetId}
Error message
findSort: item not found: ${targetId} What it means
findSortDown (part of the findSort helper in budget/util.ts) locates the item after `targetId` in a list to compute the new sort ordering when moving a category or category group down. It throws this error when `targetId` is not present in the array, meaning the caller asked to reorder an item the list does not contain. This is an internal consistency check — the reorder request referenced a stale or foreign id.
Source
Thrown at packages/desktop-client/src/components/budget/util.ts:140
? negativeColorToUse
: value === 0
? zeroColorToUse
: positiveColorToUse,
};
}
export function findSortDown<T extends { id: string }>(
arr: T[],
pos: DropPosition | null,
targetId: string,
) {
if (pos === 'top') {
return { targetId };
} else {
const idx = arr.findIndex(item => item.id === targetId);
if (idx === -1) {
throw new Error('findSort: item not found: ' + targetId);
}
const newIdx = idx + 1;
if (newIdx < arr.length) {
return { targetId: arr[newIdx].id };
} else {
// Move to the end
return { targetId: null };
}
}
}
export function findSortUp<T extends { id: string }>(
arr: T[],
pos: DropPosition | null,
targetId: string,
) {
if (pos === 'bottom') {View on GitHub (pinned to d4334cb6e6)
Solutions
- Check the targetId in the error against the current list of category/group ids to confirm it is stale or mismatched.
- Re-fetch/sync the latest budget state and retry the reorder after the list is current.
- Ensure _onReorderCategory receives a category id and _onReorderGroup receives a group id — not swapped.
- Guard the reorder handler to no-op when the target id is absent instead of throwing.
Example fix
// before _onReorderCategory(id, 'top', targetId); // after (defensive guard in caller) if (!categories.some(c => c.id === targetId)) return; _onReorderCategory(id, 'top', targetId);
Defensive patterns
Strategy: validation
Validate before calling
if (!categories.some(c => c.id === targetId)) {
// item gone (deleted locally or via sync): skip the reorder
return;
}
_onReorderCategory(id, 'top', targetId); Type guard
function listContainsId<T extends { id: string }>(arr: readonly T[], id: string): boolean {
return arr.some(item => item.id === id);
} Try / catch
try {
const { targetId: nextId } = findSortDown(list, pos, targetId);
applyReorder(nextId);
} catch (err) {
if (String(err).includes('findSort: item not found')) {
refreshBudgetList(); // resync then let the user retry
return;
}
throw err;
} Prevention
- Re-read the latest category/group list from the store inside reorder handlers, not from stale closure state.
- Keep category ids and group ids in distinct typed variables to avoid swapping them.
- After sync/deletion events, cancel or re-validate pending drag-and-drop operations.
- Prefer a no-op on missing items over throwing inside UI event handlers.
When it happens
Trigger: Calling _onReorderCategory or _onReorderGroup (budget screen drag-and-drop reorder) with a targetId that no longer exists in the current categories/groups array — e.g. the item was deleted in another tab/device before the reorder was applied, or the id passed belongs to a different entity type (group id vs category id mixup).
Common situations: Concurrent budget edits across synced devices (item deleted while a drag is in flight); stale React state where the list was rebuilt but the reorder handler captured old ids; a bug passing groupId where categoryId is expected (or vice versa).
Related errors
- Error importing budget: ${result.error}
- Error importing budget: no budget was loaded
- Error exporting budget: ${result.error}
- Error exporting budget: no data was returned
- 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/cd59d1442eb7fc05.
Report an issue: GitHub.