{"record":{"id":"cd59d1442eb7fc05","repo":"actualbudget/actual","slug":"findsort-item-not-found-targetid","errorCode":null,"errorMessage":"findSort: item not found: ${targetId}","messagePattern":"findSort: item not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/budget/util.ts","lineNumber":140,"sourceCode":"        ? negativeColorToUse\n        : value === 0\n          ? zeroColorToUse\n          : positiveColorToUse,\n  };\n}\n\nexport function findSortDown<T extends { id: string }>(\n  arr: T[],\n  pos: DropPosition | null,\n  targetId: string,\n) {\n  if (pos === 'top') {\n    return { targetId };\n  } else {\n    const idx = arr.findIndex(item => item.id === targetId);\n\n    if (idx === -1) {\n      throw new Error('findSort: item not found: ' + targetId);\n    }\n\n    const newIdx = idx + 1;\n    if (newIdx < arr.length) {\n      return { targetId: arr[newIdx].id };\n    } else {\n      // Move to the end\n      return { targetId: null };\n    }\n  }\n}\n\nexport function findSortUp<T extends { id: string }>(\n  arr: T[],\n  pos: DropPosition | null,\n  targetId: string,\n) {\n  if (pos === 'bottom') {","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/budget/util.ts#L122-L158","documentation":"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.","triggerScenarios":"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).","commonSituations":"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).","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."],"exampleFix":"// before\n_onReorderCategory(id, 'top', targetId);\n// after (defensive guard in caller)\nif (!categories.some(c => c.id === targetId)) return;\n_onReorderCategory(id, 'top', targetId);","handlingStrategy":"validation","validationCode":"if (!categories.some(c => c.id === targetId)) {\n  // item gone (deleted locally or via sync): skip the reorder\n  return;\n}\n_onReorderCategory(id, 'top', targetId);","typeGuard":"function listContainsId<T extends { id: string }>(arr: readonly T[], id: string): boolean {\n  return arr.some(item => item.id === id);\n}","tryCatchPattern":"try {\n  const { targetId: nextId } = findSortDown(list, pos, targetId);\n  applyReorder(nextId);\n} catch (err) {\n  if (String(err).includes('findSort: item not found')) {\n    refreshBudgetList(); // resync then let the user retry\n    return;\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["budget","reorder","stale-state","ui"],"backgroundTag":"stale-reference-id","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}