actualbudget/actual · error

Category "${raw}" is not found in available income categorie

Error message

Category "${raw}" is not found in available income categories

What it means

A `#template 5% of <category>` style percentage template references a source category that does not exist among the budget's income categories (or recognized special sources like `all income` / `available funds`). The engine validates every referenced name during init and throws when the raw string matches neither a known special source, an income category name, nor an income category id.

Source

Thrown at packages/loot-core/src/server/budget/category-template-context.ts:562

      incomeCategories.map(c => c.name.toLocaleLowerCase()),
    );
    const availIds = new Set(incomeCategories.map(c => c.id));

    const specialSources = new Set(['all income', 'available funds']);

    pt.forEach(t => {
      const raw = t.category;
      const lowered = raw.toLocaleLowerCase();
      // Accept either an income category name (text templates) or an income
      // category id (UI-managed templates from CategoryAutocomplete).
      if (
        specialSources.has(lowered) ||
        availNames.has(lowered) ||
        availIds.has(raw)
      ) {
        return;
      }
      throw new Error(
        `Category \x22${raw}\x22 is not found in available income categories`,
      );
    });
  }

  private checkLimit(templates: Template[]) {
    for (const template of templates.filter(
      t =>
        t.type === 'simple' ||
        t.type === 'periodic' ||
        t.type === 'limit' ||
        t.type === 'remainder',
    )) {
      let limitDef;
      if (template.type === 'limit') {
        limitDef = template;
      } else {
        if (template.limit) {

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Correct the template to use the exact name of an existing income category
  2. Mark the intended category as an income category if it should be one
  3. Use a recognized special source such as `all income` or `available funds` instead

Example fix

// before
#template 10% of Income
// after
#template 10% of Paychecks
Defensive patterns

Strategy: validation

Validate before calling

const incomeNames = new Set(categories.filter(c => c.is_income).map(c => c.name.toLowerCase()));
const src = 'Income';
if (!incomeNames.has(src.toLowerCase())) throw new Error(`percentage source not an income category: ${src}`);

Type guard

function isIncomeCategory(name, categories) {
  const c = categories.find(c => c.name.toLowerCase() === name.toLowerCase());
  return !!c && c.is_income;
}

Try / catch

try {
  await init();
} catch (e) {
  if (e.message.includes('is not found in available income categories')) {
    // fix the category name in the percent template or flag the category as income
  } else throw e;
}

Prevention

When it happens

Trigger: `checkPercentage` (called from init) processes a percent template whose source category, after lowercasing, is not in `specialSources`, `availNames`, and not in `availIds` — e.g. `#template 10% of Income` when the category is actually named "Paychecks" or is not flagged as an income category.

Common situations: Category renamed after the template was written; referencing a non-income (expense) category; typo or differing capitalization/spelling in the category name; template copied from another budget with different category names.

Related errors


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