actualbudget/actual · error
Internal error: category with ID ${targetCategoryId} not fou
Error message
Internal error: category with ID ${targetCategoryId} not found. What it means
IncomeCategoryList throws this while handling a drag-and-drop category reorder: after a drop event it searches the local `categories` array for the drop target's ID and throws when `findIndex` returns -1. It is an invariant assertion meaning the UI's drag payload referenced a category that is no longer present in the rendered category list. Actual Budget throws it to fail fast rather than silently corrupt the category ordering.
Source
Thrown at packages/desktop-client/src/components/mobile/budget/IncomeCategoryList.tsx:83
`Internal error: category ${categoryIdToMove} is not in a group and cannot be moved.`,
);
}
const targetCategoryId = e.target.key as CategoryEntity['id'];
if (e.target.dropPosition === 'before') {
moveCategory.mutate({
id: categoryToMove.id,
groupId: categoryToMove.group,
targetId: targetCategoryId,
});
} else if (e.target.dropPosition === 'after') {
const targetCategoryIndex = categories.findIndex(
c => c.id === targetCategoryId,
);
if (targetCategoryIndex === -1) {
throw new Error(
`Internal error: category with ID ${targetCategoryId} not found.`,
);
}
const nextToTargetCategory = categories[targetCategoryIndex + 1];
moveCategory.mutate({
id: categoryToMove.id,
groupId: categoryToMove.group,
// Due to the way `moveCategory` works, we use the category next to the
// actual target category here because `moveCategory` always shoves the
// category *before* the target category.
// On the other hand, using `null` as `targetId` moves the category
// to the end of the list.
targetId: nextToTargetCategory?.id || null,
});
}
},View on GitHub (pinned to d4334cb6e6)
Solutions
- Refresh the category list / reload the budget so the local `categories` array matches the server, then retry the drag.
- Check that the drag handler passes the target category's real `id` (not a group id or display index).
- Guard the reorder: if `targetCategoryIndex === -1`, log and return instead of rethrowing in production.
- If reproducible, file a bug with the category IDs involved — it indicates state desync between the drag layer and category state.
Example fix
// before
const targetCategoryIndex = categories.findIndex(c => c.id === targetCategoryId);
if (targetCategoryIndex === -1) {
throw new Error(`Internal error: category with ID ${targetCategoryId} not found.`);
}
// after
const targetCategoryIndex = categories.findIndex(c => c.id === targetCategoryId);
if (targetCategoryIndex === -1) {
console.warn('Drop target category no longer exists:', targetCategoryId);
return; // gracefully ignore stale drag
} Defensive patterns
Strategy: validation
Validate before calling
// before initiating/consuming a drag on a category
const exists = categories.some(c => c.id === targetCategoryId);
if (!exists) {
refreshCategories(); // reload state before allowing the drop
return;
} Type guard
function isKnownCategory(id: string, categories: { id: string }[]): boolean {
return categories.some(c => c.id === id);
} Try / catch
try {
onDrop(e);
} catch (err) {
if (String(err).includes('not found')) {
refreshCategories();
} else {
throw err;
}
} Prevention
- Re-fetch categories after any delete/undo operation before allowing further drag-and-drop.
- Never cache category IDs across budget reloads; resolve IDs at interaction time.
- Clear active drag state when the underlying list changes.
When it happens
Trigger: A drag-and-drop `onDrop`-style handler fires with `e.target.dropPosition === 'after'` and the drop target's `targetCategoryId` does not exist in the `categories` array in state — e.g. the category was deleted or hidden by another action/device while the drag was in flight, or a stale drag payload references an old category ID.
Common situations: Users dragging a category onto another category that a concurrent sync or undo just removed; a stale mobile UI after category deletion on another device; a bug where the drag source passes a groupId or transient ID instead of a real category id.
Related errors
- Internal error: account with ID ${targetAccountId} not found
- Internal error: category with ID ${categoryIdToMove} not fou
- Internal error: category with ID ${targetCategoryId} not fou
- Internal error: category group with ID ${groupIdToMove} not
- Internal error: category group with ID ${targetGroupId} not
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/4b86521eb40be619.
Report an issue: GitHub.