{"record":{"id":"800477a22892b827","repo":"actualbudget/actual","slug":"tracking-budget-forecasts-require-a-tracking-budge","errorCode":null,"errorMessage":"Tracking budget forecasts require a Tracking Budget file.","messagePattern":"Tracking budget forecasts require a Tracking Budget file\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/forecast/app.ts","lineNumber":78,"sourceCode":"  conditionsOp,\n  startDate,\n  endDate,\n  includeAccountlessSchedules,\n  source = 'schedules',\n}: ForecastRequestParams): Promise<ForecastResult> {\n  const includeUnassigned = includeAccountlessSchedules ?? false;\n  const dateContext = buildForecastDateContext(startDate, endDate);\n\n  if (source === 'tracking-budget') {\n    const { value: budgetType = 'envelope' } =\n      (await db.first<Pick<db.DbPreference, 'value'>>(\n        `SELECT value FROM preferences WHERE id = ?`,\n        ['budgetType'],\n      )) ?? {};\n\n    if (budgetType !== 'tracking') {\n      throw new Error(\n        'Tracking budget forecasts require a Tracking Budget file.',\n      );\n    }\n\n    const accounts = await resolveForecastAccounts({\n      accountIds: undefined,\n      plainConditions: [],\n      resolvedConditionsOp: 'and',\n      canRestrictAccounts: false,\n    });\n    const { dataPoints, lowestBalance } = projectTrackingBudgetForecast({\n      accounts,\n      dateContext,\n    });\n\n    return {\n      dataPoints,\n      lowestBalance,\n      forecastStartDate: dateContext.forecastStartDate,","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/forecast/app.ts#L60-L96","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","solutions":["Switch the budget to a Tracking Budget file (Settings > Show budget type / create or load a tracking budget) before running forecasts.","Insert/set the preference if appropriate: UPDATE preferences SET value='tracking' WHERE id='budgetType' — only if the budget genuinely is a tracking budget.","Guard automation: read the budgetType preference first and skip or branch forecast logic for non-tracking budgets.","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."],"exampleFix":"// before\nawait aql.query('forecast-run'); // throws on non-tracking budgets\n// after\nconst prefs = await aql.query('get-budget'); // or read preferences\nif (budgetType !== 'tracking') {\n  console.warn('Forecasting requires a Tracking Budget; skipping.');\n} else {\n  await aql.query('forecast-run');\n}","handlingStrategy":"validation","validationCode":"// before invoking forecast, check the budget type preference\nconst budgetType = (await db.first('SELECT value FROM preferences WHERE id = ?', ['budgetType']))?.value;\nif (budgetType !== 'tracking') {\n  throw new Error('Forecasting requires a Tracking Budget; load one first.');\n}","typeGuard":"function isTrackingBudget(budgetType: string | null | undefined): budgetType is 'tracking' {\n  return budgetType === 'tracking';\n}","tryCatchPattern":"try {\n  await aql.query('forecast-run');\n} catch (e) {\n  if (e.message.includes('Tracking Budget')) {\n    // prompt the user to switch to / create a tracking budget\n  } else throw e;\n}","preventionTips":["Check the budgetType preference before any forecast automation","Document that forecasts are tracking-budget-only in plugin/script docs","Handle legacy budgets with no budgetType preference explicitly instead of assuming a type"],"tags":["forecast","budget-type","configuration","precondition"],"backgroundTag":"unsupported-budget-type","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}