actualbudget/actual · error · APIError
Creating a category: groupId is required
Error message
Creating a category: groupId is required
What it means
createCategory requires a category group id; if the groupId parameter is falsy (undefined, null, empty string) it throws this APIError before touching the database. Categories in Actual must belong to a group, so this is a required-parameter guard.
Source
Thrown at packages/loot-core/src/server/budget/app.ts:321
}
}
return values;
}
async function createCategory({
name,
groupId,
isIncome,
hidden,
}: {
name: string;
groupId: CategoryGroupEntity['id'];
isIncome?: boolean;
hidden?: boolean;
}): Promise<CategoryEntity['id']> {
if (!groupId) {
throw APIError('Creating a category: groupId is required');
}
return await db.insertCategory({
name: name.trim(),
cat_group: groupId,
is_income: isIncome ? 1 : 0,
hidden: hidden ? 1 : 0,
});
}
async function updateCategory(category: CategoryEntity): Promise<void> {
try {
await db.updateCategory(
categoryModel.toDb({
...category,
name: category.name.trim(),
}),
);View on GitHub (pinned to d4334cb6e6)
Solutions
- Resolve a valid category group id first (e.g. via getBudgetData or createCategoryGroup) and pass it as groupId.
- Guard the group lookup result before calling createCategory.
- Create the group if it doesn't exist before creating categories in it.
Example fix
// before
await createCategory({ name: 'Groceries' });
// after
const groupId = await createCategoryGroup({ name: 'Essentials' });
await createCategory({ name: 'Groceries', groupId }); Defensive patterns
Strategy: validation
Validate before calling
const groups = await actual.getBudgetData([], ['category_groups']);
const groupId = groups.category_groups.find(g => g.name === 'Essentials').id;
if (!groupId) throw new Error('Group not found; create it first');
await actual.createCategory({ name: 'Groceries', groupId }); Type guard
function isValidGroupId(v) {
return typeof v === 'string' && v.length > 0;
} Try / catch
try {
await actual.createCategory({ name, groupId });
} catch (e) {
if (String(e.message).includes('groupId is required')) {
console.error('Resolve/create the category group first');
} else throw e;
} Prevention
- Always resolve the group id before creating categories
- Never forward a possibly-undefined lookup result unchecked
- Create the group in the same script if missing
When it happens
Trigger: Calling createCategory without groupId, or with an empty/undefined value — e.g. forgetting to resolve the group id first, or forwarding the result of a failed group lookup.
Common situations: Scripting the API and omitting the groupId field; passing a group name instead of its id; a group-id lookup returning undefined and being passed unchecked.
Understand the failure class
Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.
Related errors
- No update fields provided. Use --name or --hidden.
- Unknown payee name normalization: ${String(normalization)}
- `date` is required when adding a transaction
- Amount is invalid, must be an integer: ${trans.amount}
- Category '${category.name}' already exists in group '${categ
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/936119e5b3694024.
Report an issue: GitHub.