{"record":{"id":"13aa0a0623961fb3","repo":"actualbudget/actual","slug":"unrecognized-periodic-period-string-period","errorCode":null,"errorMessage":"Unrecognized periodic period: ${String(period)}","messagePattern":"Unrecognized periodic period: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/budget/category-template-context.ts","lineNumber":733,"sourceCode":"\n    let dateShiftFunction;\n    switch (period) {\n      case 'day':\n        dateShiftFunction = monthUtils.addDays;\n        break;\n      case 'week':\n        dateShiftFunction = monthUtils.addWeeks;\n        break;\n      case 'month':\n        dateShiftFunction = monthUtils.addMonths;\n        break;\n      case 'year':\n        // the addYears function doesn't return the month number, so use addMonths\n        dateShiftFunction = (date: string | Date, numPeriods: number) =>\n          monthUtils.addMonths(date, numPeriods * 12);\n        break;\n      default:\n        throw new Error(`Unrecognized periodic period: ${String(period)}`);\n    }\n\n    //shift the starting date until its in our month or in the future\n    while (templateContext.month > date) {\n      date = dateShiftFunction(date, numPeriods);\n    }\n\n    if (\n      monthUtils.differenceInCalendarMonths(templateContext.month, date) < 0\n    ) {\n      return 0;\n    } // nothing needed this month\n\n    const nextMonth = monthUtils.addMonths(templateContext.month, 1);\n    while (date < nextMonth) {\n      toBudget += amount;\n      date = dateShiftFunction(date, numPeriods);\n    }","sourceCodeStart":715,"sourceCodeEnd":751,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/budget/category-template-context.ts#L715-L751","documentation":"This error is thrown by CategoryTemplateContext when processing a `periodic` budget template whose `period` option is not one of the supported values (day, week, month, year). The switch statement maps each period to a date-shift function; the default branch rejects anything else. It is a template-definition validation error raised at budget-template application time.","triggerScenarios":"A budget template line like `#template periodic 100 every 3 fortnights` or any misspelled period (e.g. `weekly`, `montly`, `yr`) is parsed and passed as `period` to the periodic template builder, hitting the switch default.","commonSituations":"Typos in template comments in category notes, copy-pasted templates from older docs or other tools, localized period names, or a renamed/removed period keyword after a version change.","solutions":["Change the template line to use a supported period: day, week, month, or year (e.g. `#template periodic 100 every 1 month`).","Check spelling/normalization of the period string before applying templates.","If programmatically building templates, validate the period against the allowed set before calling the template context.","If a new period seems needed, verify the installed Actual version supports it or open a feature request."],"exampleFix":"// before\n#template periodic 50 every 2 weeks-ish\n// after\n#template periodic 50 every 2 weeks","handlingStrategy":"validation","validationCode":"const PERIODS = ['day', 'week', 'month', 'year'] as const;\ntype Period = (typeof PERIODS)[number];\nif (!PERIODS.includes(period as Period)) {\n  throw new Error(`period must be one of ${PERIODS.join(', ')}, got: ${period}`);\n}","typeGuard":"function isPeriod(p: unknown): p is 'day' | 'week' | 'month' | 'year' {\n  return typeof p === 'string' && ['day', 'week', 'month', 'year'].includes(p);\n}","tryCatchPattern":"try {\n  await applyTemplates(categoryIds, month);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Unrecognized periodic period')) {\n    logger.warn('Skipping category with invalid periodic template', { error: e.message });\n  } else {\n    throw e;\n  }\n}","preventionTips":["Only use day/week/month/year in #template periodic lines","Validate template notes before saving them in category notes","Keep templates copied from current official docs, not old gists","Add a pre-apply lint step that regex-checks periodic template lines"],"tags":["budget-template","validation","config"],"backgroundTag":"invalid-template-option","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}