actualbudget/actual · error · APIError
${debug}: category id is required
Error message
${debug}: category id is required What it means
validateExpenseCategory throws APIError when the category id argument is null or undefined. API methods that operate on expense categories require a concrete category id to look up in the database. The debug prefix identifies which API call failed.
Source
Thrown at packages/loot-core/src/server/api.ts:106
let handlers = {} as unknown as Handlers;
async function validateMonth(month) {
if (!month.match(/^\d{4}-\d{2}$/)) {
throw APIError('Invalid month format, use YYYY-MM: ' + month);
}
if (!IMPORT_MODE) {
const { start, end } = await handlers['get-budget-bounds']();
const range = monthUtils.range(start, end);
if (!range.includes(month)) {
throw APIError('No budget exists for month: ' + month);
}
}
}
async function validateExpenseCategory(debug, id) {
if (id == null) {
throw APIError(`${debug}: category id is required`);
}
const row = await db.first<Pick<db.DbCategory, 'is_income'>>(
'SELECT is_income FROM categories WHERE id = ?',
[id],
);
if (!row) {
throw APIError(`${debug}: category "${id}" does not exist`);
}
if (row.is_income !== 0) {
throw APIError(`${debug}: category "${id}" is not an expense category`);
}
}
function checkFileOpen() {
if (!(prefs.getPrefs() || {}).id) {View on GitHub (pinned to d4334cb6e6)
Solutions
- Supply a valid expense category id obtained from q.getCategories()
- Default the parameter to a real category id (e.g. an 'Uncategorized' category) before calling
- Guard the call site: if (categoryId == null) resolve or reject before invoking the API
- For transactions that genuinely have no category, use the appropriate API path for uncategorized transactions rather than null category ids
Example fix
// before
await q.createTransaction({ accountId, date, amount: 2500, category: null });
// APIError: createTransaction: category id is required
// after
const [category] = await q.getCategories();
await q.createTransaction({ accountId, date, amount: 2500, category: category.id }); Defensive patterns
Strategy: validation
Validate before calling
if (categoryId == null) {
throw new Error('categoryId is required before calling expense-category APIs');
} Type guard
function hasCategoryId(tx) {
return typeof tx.category === 'string' && tx.category.length > 0;
} Prevention
- Resolve a default/uncategorized expense category id at startup
- Validate input objects for required category fields before API calls
- Never rely on possibly-null lookups as category ids
- In importers, map empty category columns to a real category id
When it happens
Trigger: Calling methods like createTransaction or setBudgetAmount with categoryId undefined — e.g. destructuring an object whose field is missing, passing null explicitly, or omitting the category parameter for a categorized transaction.
Common situations: Spreadsheets/CSV importers that leave the category column empty; API clients that send JSON without the category key; code that passes a variable which is null because a prior lookup failed silently.
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
- ${debug}: category "${id}" does not exist
- ${debug}: category "${id}" is not an expense category
- No fields to update. Use --name to specify a new name.
- No valid payee IDs provided in --ids. Provide comma-separate
- --table is required (or use --file or --last)
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/53f547020299da61.
Report an issue: GitHub.