actualbudget/actual · error

Tracking budget forecasts require a Tracking Budget file.

Error message

Tracking budget forecasts require a Tracking Budget file.

What it means

The forecast app (packages/loot-core/src/server/forecast/app.ts) only supports forecast runs against a Tracking Budget. generateForecast reads the 'budgetType' preference; if it is not exactly 'tracking', it throws this error before resolving forecast accounts. Forecasting depends on tracking-budget account structures and is intentionally unavailable for non-tracking (e.g. envelope/budget-type 'report') budgets.

Source

Thrown at packages/loot-core/src/server/forecast/app.ts:78

  conditionsOp,
  startDate,
  endDate,
  includeAccountlessSchedules,
  source = 'schedules',
}: ForecastRequestParams): Promise<ForecastResult> {
  const includeUnassigned = includeAccountlessSchedules ?? false;
  const dateContext = buildForecastDateContext(startDate, endDate);

  if (source === 'tracking-budget') {
    const { value: budgetType = 'envelope' } =
      (await db.first<Pick<db.DbPreference, 'value'>>(
        `SELECT value FROM preferences WHERE id = ?`,
        ['budgetType'],
      )) ?? {};

    if (budgetType !== 'tracking') {
      throw new Error(
        'Tracking budget forecasts require a Tracking Budget file.',
      );
    }

    const accounts = await resolveForecastAccounts({
      accountIds: undefined,
      plainConditions: [],
      resolvedConditionsOp: 'and',
      canRestrictAccounts: false,
    });
    const { dataPoints, lowestBalance } = projectTrackingBudgetForecast({
      accounts,
      dateContext,
    });

    return {
      dataPoints,
      lowestBalance,
      forecastStartDate: dateContext.forecastStartDate,

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Switch the budget to a Tracking Budget file (Settings > Show budget type / create or load a tracking budget) before running forecasts.
  2. Insert/set the preference if appropriate: UPDATE preferences SET value='tracking' WHERE id='budgetType' — only if the budget genuinely is a tracking budget.
  3. Guard automation: read the budgetType preference first and skip or branch forecast logic for non-tracking budgets.
  4. If budgetType is missing on a legacy file, set it explicitly to the correct type via the app rather than editing the DB by hand.

Example fix

// before
await aql.query('forecast-run'); // throws on non-tracking budgets
// after
const prefs = await aql.query('get-budget'); // or read preferences
if (budgetType !== 'tracking') {
  console.warn('Forecasting requires a Tracking Budget; skipping.');
} else {
  await aql.query('forecast-run');
}
Defensive patterns

Strategy: validation

Validate before calling

// before invoking forecast, check the budget type preference
const budgetType = (await db.first('SELECT value FROM preferences WHERE id = ?', ['budgetType']))?.value;
if (budgetType !== 'tracking') {
  throw new Error('Forecasting requires a Tracking Budget; load one first.');
}

Type guard

function isTrackingBudget(budgetType: string | null | undefined): budgetType is 'tracking' {
  return budgetType === 'tracking';
}

Try / catch

try {
  await aql.query('forecast-run');
} catch (e) {
  if (e.message.includes('Tracking Budget')) {
    // prompt the user to switch to / create a tracking budget
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the forecast endpoints (generateForecast, reached via 'forecast' mutators such as result/combinedResult/savingsOnlyResult) while the budget file's preferences row for id 'budgetType' is missing or set to anything other than 'tracking'.

Common situations: Running forecasts on an older budget file created before tracking budgets existed (no budgetType preference at all, so the query returns {} and ?? {} yields undefined !== 'tracking'); users switching budget types; plugins or scripts invoking forecast APIs on the wrong budget.

Related errors


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