{"record":{"id":"299a0e0d280ed688","repo":"actualbudget/actual","slug":"target-month-has-passed-remove-or-update-the-targ","errorCode":null,"errorMessage":"Target month has passed, remove or update the target month","messagePattern":"Target month has passed, remove or update the target month","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/budget/category-template-context.ts","lineNumber":530,"sourceCode":"      .filter(t => t.type === 'schedule' || t.type === 'by')\n      .forEach(t => {\n        if (t.priority !== lowestPriority) {\n          throw new Error(\n            `Schedule and By templates must be the same priority level. Fix by setting all Schedule and By templates to priority level ${lowestPriority}`,\n          );\n          //t.priority = lowestPriority;\n        }\n      });\n    // check if the target date is past and not repeating\n    templates\n      .filter(t => t.type === 'by' || t.type === 'spend')\n      .forEach(t => {\n        const range = monthUtils.differenceInCalendarMonths(\n          `${t.month}`,\n          month,\n        );\n        if (range < 0 && !(t.repeat || t.annual)) {\n          throw new Error(\n            `Target month has passed, remove or update the target month`,\n          );\n        }\n      });\n  }\n\n  static async checkPercentage(templates: Template[]) {\n    const pt = templates.filter(t => t.type === 'percentage');\n    if (pt.length === 0) return;\n\n    const availCategories = await db.getCategories();\n    const incomeCategories = availCategories.filter(c => c.is_income);\n    const availNames = new Set(\n      incomeCategories.map(c => c.name.toLocaleLowerCase()),\n    );\n    const availIds = new Set(incomeCategories.map(c => c.id));\n\n    const specialSources = new Set(['all income', 'available funds']);","sourceCodeStart":512,"sourceCodeEnd":548,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/budget/category-template-context.ts#L512-L548","documentation":"A `by` template targets a month that is already in the past, and the template is not repeating. The engine throws because the target can never be reached and the template would have no effect.","triggerScenarios":"`checkByAndScheduleAndSpend` (init) computes `differenceInCalendarMonths(t.month, currentMonth) < 0` for a template where neither `repeat` nor `annual` is set — e.g. `#template by 2024-06` evaluated in a later month.","commonSituations":"Leaving an old one-time savings-goal template in place after its target date passed; template notes copied from an old budget; annual goals mistakenly written without the `annual`/`repeat` keyword.","solutions":["Update the target month in the template to a current or future month","Add `repeat each` or make it an `annual` template if the goal should recur","Remove the outdated `#template by ...` line if the goal is complete"],"exampleFix":"// before\n#template by 2024-06\n// after\n#template by 2027-01","handlingStrategy":"validation","validationCode":"const target = '2024-06';\nconst diff = monthUtils.differenceInCalendarMonths(target, monthUtils.currentMonth());\nif (diff < 0) throw new Error(`by-target ${target} is in the past; add repeat/annual or update the month`);","typeGuard":"function isReachingableTarget(t, month) {\n  return monthUtils.differenceInCalendarMonths(String(t.month), month) >= 0 || Boolean(t.repeat) || Boolean(t.annual);\n}","tryCatchPattern":"try {\n  await init();\n} catch (e) {\n  if (e.message.includes('Target month has passed')) {\n    // update the by-month, add repeat/annual, or delete the line\n  } else throw e;\n}","preventionTips":["Audit `#template by` lines at the start of each year","Use `repeat each` or `annual` for recurring goals","Remove templates once their savings target is met"],"tags":["goal-templates","date","validation"],"backgroundTag":"expired-target-date","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}