{"record":{"id":"7abc9b75701b3851","repo":"actualbudget/actual","slug":"account-group-name-is-required","errorCode":null,"errorMessage":"Account group name is required","messagePattern":"Account group name is required","errorType":"validation","errorClass":"APIError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/api.ts","lineNumber":692,"sourceCode":"handlers['api/account-groups-get'] = async function () {\n  checkFileOpen();\n  const groups = await handlers['account-groups-get']();\n  return groups.map(group => accountGroupModel.toExternal(group));\n};\n\nhandlers['api/account-group-create'] = withMutation(async function ({ group }) {\n  checkFileOpen();\n  return handlers['account-group-create']({ name: group.name });\n});\n\nhandlers['api/account-group-update'] = withMutation(async function ({\n  id,\n  fields,\n}) {\n  checkFileOpen();\n  const group = accountGroupModel.fromExternal(fields);\n  if (group.name == null) {\n    throw APIError('Account group name is required');\n  }\n  return handlers['account-group-update']({ id, name: group.name });\n});\n\nhandlers['api/account-group-delete'] = withMutation(async function ({ id }) {\n  checkFileOpen();\n  await handlers['account-group-delete']({ id });\n});\n\nhandlers['api/categories-get'] = async function ({\n  hidden,\n}: { hidden?: boolean } = {}) {\n  checkFileOpen();\n  const result = await handlers['get-categories']({ hidden });\n  return result.list.map(category => categoryModel.toExternal(category));\n};\n\nhandlers['api/category-groups-get'] = async function ({","sourceCodeStart":674,"sourceCodeEnd":710,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/api.ts#L674-L710","documentation":"api.accountGroupUpdate() converts the provided fields via the account-group model's fromExternal() and requires a group name. If the resulting group.name is null/undefined (i.e. no name field was supplied), the update would blank or invalidate the group, so the API throws this APIError.","triggerScenarios":"Calling api.accountGroupUpdate(id, fields) where fields omits 'name' or passes name: null/undefined.","commonSituations":"Partially-patch-style updates where the caller assumes only changed fields are needed; forms that submit empty names; destructuring/serialization dropping the name key.","solutions":["Always include a non-null name in the fields object (use the current group's name if only toggling other attributes)","Fetch the group first and spread it: api.accountGroupUpdate(id, { ...existing, name: existing.name })","Validate fields.name is a non-empty string before calling"],"exampleFix":"// before\nawait api.accountGroupUpdate(id, { cash: 1000 });\n// after\nconst group = await api.accountGroupGet(id);\nawait api.accountGroupUpdate(id, { ...group, cash: 1000, name: group.name });","handlingStrategy":"validation","validationCode":"const group = accountGroupModel.fromExternal(fields);\nif (group.name == null) throw APIError('Account group name is required');","typeGuard":"function hasValidName(g) {\n  return g.name != null && String(g.name).length > 0;\n}","tryCatchPattern":"try {\n  await updateAccountGroup(args);\n} catch (e) {\n  if (/name is required/.test(e.message)) {\n    throw new UserInputError('name is required for account groups');\n  }\n  throw e;\n}","preventionTips":["Make name mandatory in the external input schema (zod/JSON schema)","Require full-group payloads for updates","Reject empty names at the boundary"],"tags":["api","validation","required-field"],"backgroundTag":"missing-required-field","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}