actualbudget/actual · error
Only one spend template is allowed per category
Error message
Only one spend template is allowed per category
What it means
Only one `spend` template may exist per category per month. `checkSpend`, invoked from the CategoryTemplateContext constructor, counts templates of type 'spend' for the category and throws if more than one is present, since multiple spend goals would be ambiguous.
Solutions
- Delete one of the spend template lines so only a single spend goal remains for that category
- Merge the two goals into one spend template with a single target amount and date
Example fix
// before #template spend 200 #template spend 300 // after #template spend 300
Defensive patterns
Strategy: validation
Validate before calling
const spendLines = noteLines.filter(l => /#template\s+spend/.test(l));
if (spendLines.length > 1) throw new Error('only one spend template per category'); Try / catch
try {
await init();
} catch (e) {
if (e.message === 'Only one spend template is allowed per category') {
// delete or merge one of the spend lines in the category note
} else throw e;
} Prevention
- Update the existing spend line rather than adding a second
- Search notes for `#template spend` before adding one
- Remember `by` templates also imply spend; avoid pairing with an explicit spend line
When it happens
Trigger: A category's templates for the month contain two or more `#template spend ...` (or `template up to spend` variants) lines, e.g. `#template spend 200` and `#template spend from 2024-01-01` both active in the same category.
Common situations: Adding a new spend goal without deleting the previous one; combined `by`-derived spend plus an explicit spend line; template notes copied from another category that already had one.
Related errors
- Only one #goal is allowed per category
- Only one `up to` allowed per category
- An ' ' account group already exists.
- Category ' ' already exists in group
- Category " " is not found in available income categories
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/66260a1ee6eb7fdc.
Report an issue: GitHub.
Appendix: source
Thrown at packages/loot-core/src/server/budget/category-template-context.ts:646
if (this.fromLastMonth >= this.limitAmount) {
this.limitMet = true;
if (this.limitHold) {
this.limitExcess = 0;
this.toBudgetAmount = 0;
this.fullAmount = 0;
} else {
this.limitExcess = this.fromLastMonth - this.limitAmount;
this.toBudgetAmount = -this.limitExcess;
this.fullAmount = -this.limitExcess;
}
}
}
}
private checkSpend() {
const st = this.templates.filter(t => t.type === 'spend');
if (st.length > 1) {
throw new Error('Only one spend template is allowed per category');
}
}
private checkGoal() {
if (this.goals.length > 1) {
throw new Error(`Only one #goal is allowed per category`);
}
}
private removeFraction(amount: number): number {
return amountToInteger(
Math.round(integerToAmount(amount, this.currency.decimalPlaces)),
this.currency.decimalPlaces,
);
}
//-----------------------------------------------------------------------------
// Processor FunctionsView on GitHub (pinned to d4334cb6e6)