{"record":{"id":"af1673819458eb9d","repo":"actualbudget/actual","slug":"a-category-with-the-name-category-name-alread","errorCode":null,"errorMessage":"A category with the name \"${category.name}\" already exists.","messagePattern":"A category with the name \"(.+?)\" already exists\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/budget/app.ts","lineNumber":345,"sourceCode":"    is_income: isIncome ? 1 : 0,\n    hidden: hidden ? 1 : 0,\n  });\n}\n\nasync function updateCategory(category: CategoryEntity): Promise<void> {\n  try {\n    await db.updateCategory(\n      categoryModel.toDb({\n        ...category,\n        name: category.name.trim(),\n      }),\n    );\n  } catch (e) {\n    if (\n      e instanceof Error &&\n      e.message.toLowerCase().includes('unique constraint')\n    ) {\n      throw new Error(\n        `A category with the name \"${category.name}\" already exists.`,\n        { cause: e },\n      );\n    }\n    throw e;\n  }\n}\n\nasync function moveCategory({\n  id,\n  groupId,\n  targetId,\n}: {\n  id: CategoryEntity['id'];\n  groupId: CategoryGroupEntity['id'];\n  targetId: CategoryEntity['id'] | null;\n}): Promise<void> {\n  await batchMessages(async () => {","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/budget/app.ts#L327-L363","documentation":"updateCategory translates a SQLite UNIQUE constraint violation on the categories.name column into this user-facing error, since category names must be unique within the budget.","triggerScenarios":"Renaming a category (updateCategory) to a name that already exists in the same category group.","commonSituations":"Importing categories from CSV/YNAB where duplicates exist; UI allowing rename without a prior duplicate check; case variations that collide after SQLite's case-insensitive uniqueness rules.","solutions":["Check for an existing category with the target name (case-insensitively) before renaming.","Catch this error in the UI and prompt the user to pick a different name.","If duplicates are intentional, merge/delete the existing category first."],"exampleFix":"// before\nawait updateCategory({ id, name: 'Groceries' }); // throws if 'Groceries' exists\n// after\nconst existing = await q('categories').filter({ name: 'Groceries' }).select('*');\nif (existing.length === 0) {\n  await updateCategory({ id, name: 'Groceries' });\n}","handlingStrategy":"try-catch","validationCode":"const dup = await q('categories').select('name').execute();\nconst exists = dup.some(c => c.name.toLowerCase() === newName.toLowerCase());\nif (exists) throw new Error(`A category named \"${newName}\" already exists`);","typeGuard":null,"tryCatchPattern":"try {\n  await app.updateCategory({ id, name: newName });\n} catch (e) {\n  if (e.message.includes('already exists')) {\n    showBanner(`Rename failed: a category named \"${newName}\" exists`);\n  } else throw e;\n}","preventionTips":["Check existing category names (case-insensitive) before renaming","Deduplicate names during CSV/YNAB imports","Handle the UNIQUE-constraint-mapped error in the rename UI"],"tags":["budget","category","unique-constraint","duplicate"],"backgroundTag":"duplicate-name-constraint","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}