actualbudget/actual · error
Unrecognized periodic period: ${String(period)}
Error message
Unrecognized periodic period: ${String(period)} What it means
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.
Source
Thrown at packages/loot-core/src/server/budget/category-template-context.ts:733
let dateShiftFunction;
switch (period) {
case 'day':
dateShiftFunction = monthUtils.addDays;
break;
case 'week':
dateShiftFunction = monthUtils.addWeeks;
break;
case 'month':
dateShiftFunction = monthUtils.addMonths;
break;
case 'year':
// the addYears function doesn't return the month number, so use addMonths
dateShiftFunction = (date: string | Date, numPeriods: number) =>
monthUtils.addMonths(date, numPeriods * 12);
break;
default:
throw new Error(`Unrecognized periodic period: ${String(period)}`);
}
//shift the starting date until its in our month or in the future
while (templateContext.month > date) {
date = dateShiftFunction(date, numPeriods);
}
if (
monthUtils.differenceInCalendarMonths(templateContext.month, date) < 0
) {
return 0;
} // nothing needed this month
const nextMonth = monthUtils.addMonths(templateContext.month, 1);
while (date < nextMonth) {
toBudget += amount;
date = dateShiftFunction(date, numPeriods);
}View on GitHub (pinned to d4334cb6e6)
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.
Example fix
// before #template periodic 50 every 2 weeks-ish // after #template periodic 50 every 2 weeks
Defensive patterns
Strategy: validation
Validate before calling
const PERIODS = ['day', 'week', 'month', 'year'] as const;
type Period = (typeof PERIODS)[number];
if (!PERIODS.includes(period as Period)) {
throw new Error(`period must be one of ${PERIODS.join(', ')}, got: ${period}`);
} Type guard
function isPeriod(p: unknown): p is 'day' | 'week' | 'month' | 'year' {
return typeof p === 'string' && ['day', 'week', 'month', 'year'].includes(p);
} Try / catch
try {
await applyTemplates(categoryIds, month);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Unrecognized periodic period')) {
logger.warn('Skipping category with invalid periodic template', { error: e.message });
} else {
throw e;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid config file: expected an object with keys: ${configF
- Invalid config file: unknown key "${key}"
- Invalid config file: key "${key}" must be a string, got ${ty
- Invalid config file: key "${key}" must be a non-negative int
- Invalid config file: key "${key}" must be a boolean, got ${t
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/13aa0a0623961fb3.
Report an issue: GitHub.