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

  1. Check the targetId in the error against the current list of category/group ids to confirm it is stale or mismatched.
  2. Re-fetch/sync the latest budget state and retry the reorder after the list is current.
  3. Ensure _onReorderCategory receives a category id and _onReorderGroup receives a group id — not swapped.
  4. 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

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


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/cd59d1442eb7fc05. Report an issue: GitHub.