actualbudget/actual · error

Schedule ${t.name ?? t.scheduleId} does not exist

Error message

Schedule ${t.name ?? t.scheduleId} does not exist

What it means

The category template system resolves 'schedule' templates against the set of schedules that exist in the budget. A template referencing a schedule id or name not found in the budget throws during context initialization.

Source

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

  ) {
    if (
      templates.filter(t => t.type === 'schedule' || t.type === 'by').length ===
      0
    ) {
      return;
    }
    //check schedule existence (prefer scheduleId, fall back to name)
    const activeSchedules = await getActiveSchedules();
    const scheduleIds = new Set(activeSchedules.map(s => s.id));
    const scheduleNames = new Set(
      activeSchedules.map(s => s.name?.trim()).filter(Boolean),
    );
    templates
      .filter(t => t.type === 'schedule')
      .forEach(t => {
        if (t.scheduleId) {
          if (!scheduleIds.has(t.scheduleId)) {
            throw new Error(
              `Schedule ${t.name ?? t.scheduleId} does not exist`,
            );
          }
        } else if (t.name) {
          if (!scheduleNames.has(t.name.trim())) {
            throw new Error(`Schedule ${t.name.trim()} does not exist`);
          }
        } else {
          throw new Error('Schedule template has no scheduleId or name');
        }
      });
    //find lowest priority
    const lowestPriority = Math.min(
      ...templates
        .filter(t => t.type === 'schedule' || t.type === 'by')
        .map(t => t.priority),
    );
    //warn if priority needs fixed

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Recreate the missing schedule or update the category template note to reference an existing schedule name/id.
  2. List existing schedules (q('schedules').select('*')) and match template references against them.
  3. Use schedule ids instead of names for stability across renames.

Example fix

// before
// category note: #template schedule Electric Bill   (schedule was deleted)
// after
// recreate the schedule, or update the note to:
// #template schedule Electric Bill 2024   (existing schedule name)
Defensive patterns

Strategy: validation

Validate before calling

const schedules = await q('schedules').select('name').execute();
const names = new Set(schedules.map(s => s.name.trim()));
if (!names.has(templateScheduleName)) throw new Error(`Schedule "${templateScheduleName}" not found in this budget`);

Try / catch

try {
  await app.runBudgetTemplates();
} catch (e) {
  if (e.message.includes('does not exist') && e.message.includes('Schedule')) {
    console.error('A category note references a missing schedule:', e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: A category note contains a directive like '#template schedule <name>' where <name> does not match any schedule (deleted schedule, renamed schedule, or schedule from another budget).

Common situations: Deleting or renaming a schedule after writing template directives in category notes; typos in schedule names; copying budget templates between budgets that have different schedules.

Related errors


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