actualbudget/actual · error

Invalid "categories" option for category_groups: "${categori

Error message

Invalid "categories" option for category_groups: "${categoriesOption}"

What it means

execCategoryGroups validates the 'categories' table option for category_groups queries, which must be 'all' or 'none'. Any other value is rejected before SQL execution.

Source

Thrown at packages/loot-core/src/server/aql/schema/executors.ts:269

}

// Category groups executor

type CategoriesOption = 'all' | 'none';

async function execCategoryGroups(
  compilerState: CompilerState,
  queryState: QueryState,
  sqlPieces: SqlPieces,
  params: (string | number)[],
  outputTypes: OutputTypes,
) {
  const tableOptions = queryState.tableOptions || {};
  const categoriesOption = tableOptions.categories
    ? (tableOptions.categories as string)
    : 'all';
  if (!isValidCategoriesOption(categoriesOption)) {
    throw new Error(
      `Invalid "categories" option for category_groups: "${categoriesOption}"`,
    );
  }

  if (categoriesOption !== 'none') {
    return execCategoryGroupsWithCategories(
      compilerState,
      queryState,
      sqlPieces,
      params,
      categoriesOption,
      outputTypes,
    );
  }
  return execCategoryGroupsBasic(
    compilerState,
    queryState,
    sqlPieces,

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Use 'all' or 'none' for the categories option.
  2. Validate the option before constructing the query.
  3. Omit the option to get the default 'all' behavior.

Example fix

// before
q('category_groups', { categories: 'exclude-none' });
// after
q('category_groups', { categories: 'all' });
Defensive patterns

Strategy: validation

Validate before calling

const CATEGORIES_OPTIONS = ['all','none'];
function validateCategoriesOption(o) {
  const v = o ?? 'all';
  if (!CATEGORIES_OPTIONS.includes(v)) throw new Error(`categories must be ${CATEGORIES_OPTIONS.join(' or ')}`);
}

Type guard

function isCategoriesOption(v: unknown): v is 'all'|'none' {
  return v === 'all' || v === 'none';
}

Try / catch

try {
  return await q('category_groups', { categories: opt }).select('*').execute();
} catch (e) {
  if (e.message.includes('Invalid "categories" option')) {
    return q('category_groups').select('*').execute();
  }
  throw e;
}

Prevention

When it happens

Trigger: q('category_groups', { categories: 'only' }) or any string other than 'all'/'none' (default is 'all').

Common situations: Copying the 'splits' option style from transactions queries, typos, or passing config-driven strings without validation.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/7b7f39b5d6d97e2d. Report an issue: GitHub.