{"record":{"id":"913ac91ca614140d","repo":"actualbudget/actual","slug":"an-existinggroup-name-account-group-already-e","errorCode":null,"errorMessage":"An '${existingGroup.name}' account group already exists.","messagePattern":"An '(.+?)' account group already exists\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/db/index.ts","lineNumber":802,"sourceCode":"  });\n}\n\nexport function getAccountGroups() {\n  return all<DbAccountGroup>(\n    `SELECT * FROM account_groups WHERE tombstone = 0 ORDER BY sort_order, id`,\n  );\n}\n\nexport async function insertAccountGroup(\n  group: WithRequired<Partial<DbAccountGroup>, 'name'>,\n): Promise<DbAccountGroup['id']> {\n  // Don't allow duplicate group\n  const existingGroup = await first<Pick<DbAccountGroup, 'id' | 'name'>>(\n    `SELECT id, name FROM account_groups WHERE UPPER(name) = ? AND tombstone = 0 LIMIT 1`,\n    [group.name.toUpperCase()],\n  );\n  if (existingGroup) {\n    throw new Error(`An '${existingGroup.name}' account group already exists.`);\n  }\n\n  const lastGroup = await first<Pick<DbAccountGroup, 'sort_order'>>(`\n    SELECT sort_order FROM account_groups WHERE tombstone = 0 ORDER BY sort_order DESC, id DESC LIMIT 1\n  `);\n  const sort_order = (lastGroup ? lastGroup.sort_order : 0) + SORT_INCREMENT;\n\n  group = {\n    ...accountGroupModel.validate(group),\n    sort_order,\n  };\n  const id: DbAccountGroup['id'] = await insertWithUUID(\n    'account_groups',\n    group,\n  );\n  return id;\n}\n","sourceCodeStart":784,"sourceCodeEnd":820,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/db/index.ts#L784-L820","documentation":"insertAccountGroup rejects creating an account group whose name case-insensitively matches any existing non-deleted (tombstone = 0) group. It looks up UPPER(name) in account_groups and throws if found. The thrown message includes the existing group's stored name.","triggerScenarios":"Calling insertAccountGroup with a name equal (ignoring case) to an existing live group, e.g. 'Checking' when 'CHECKING' exists; duplicate submits of the same create form; imports that carry over group names already present in the budget.","commonSituations":"Users creating account groups manually with a reused name, migrating budgets or importing CSV/QIF that recreates existing groups, retry logic re-issuing a create after a timeout.","solutions":["Use a different, unique group name","Query account_groups first for a case-insensitive match and reuse/update the existing group instead","If the previous group was deleted, note tombstoned groups are excluded so their name can be reused — the error means a live group holds the name","Catch the error and show the conflicting group name in the UI"],"exampleFix":"// before\nawait aqlQuery.insertAccountGroup({ name: 'Savings' });\n// after\nconst groups = await aqlQuery.getAccountGroups();\nif (!groups.some(g => g.name.toLowerCase() === 'savings')) {\n  await aqlQuery.insertAccountGroup({ name: 'Savings' });\n}","handlingStrategy":"validation","validationCode":"const groups = await aqlQuery.getAccountGroups();\nif (groups.some(g => g.name.toUpperCase() === newName.toUpperCase())) {\n  throw new Error(`Account group '${newName}' already exists`);\n}","typeGuard":"function isNameAvailable(groups: { name: string }[], name: string): boolean {\n  return !groups.some(g => g.name.toUpperCase() === name.toUpperCase());\n}","tryCatchPattern":"try {\n  await aqlQuery.insertAccountGroup(group);\n} catch (e) {\n  if (e.message.includes('account group already exists')) {\n    notifyUser(`A group named '${group.name}' already exists`);\n  } else throw e;\n}","preventionTips":["Always check existing group names case-insensitively before insert","Reuse or rename existing groups rather than creating near-duplicates","Dedupe imported group names during migrations","Guard against double form submission"],"tags":["validation","duplicate","account-groups","database"],"backgroundTag":"duplicate-name-conflict","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}