{"record":{"id":"e517fd6fcf84687b","repo":"actualbudget/actual","slug":"only-one-goal-is-allowed-per-category","errorCode":null,"errorMessage":"Only one #goal is allowed per category","messagePattern":"Only one #goal is allowed per category","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/budget/category-template-context.ts","lineNumber":652,"sourceCode":"        } else {\n          this.limitExcess = this.fromLastMonth - this.limitAmount;\n          this.toBudgetAmount = -this.limitExcess;\n          this.fullAmount = -this.limitExcess;\n        }\n      }\n    }\n  }\n\n  private checkSpend() {\n    const st = this.templates.filter(t => t.type === 'spend');\n    if (st.length > 1) {\n      throw new Error('Only one spend template is allowed per category');\n    }\n  }\n\n  private checkGoal() {\n    if (this.goals.length > 1) {\n      throw new Error(`Only one #goal is allowed per category`);\n    }\n  }\n\n  private removeFraction(amount: number): number {\n    return amountToInteger(\n      Math.round(integerToAmount(amount, this.currency.decimalPlaces)),\n      this.currency.decimalPlaces,\n    );\n  }\n\n  //-----------------------------------------------------------------------------\n  //  Processor Functions\n\n  static runSimple(\n    template: SimpleTemplate,\n    templateContext: CategoryTemplateContext,\n  ): number {\n    if (template.monthly != null) {","sourceCodeStart":634,"sourceCodeEnd":670,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/budget/category-template-context.ts#L634-L670","documentation":"A category may have at most one `#goal` template. `checkGoal` (called during CategoryTemplateContext construction) throws when `this.goals` holds more than one goal for the same category, because a single category cannot have conflicting goal definitions.","triggerScenarios":"Two or more `#template goal <amount>` lines exist for the same category in the same month, e.g. `#template goal 500` plus another `#template goal 1000` in the category note.","commonSituations":"Editing a goal amount by adding a new line instead of updating the existing one; leftover goal line from a previous month's note still applying; pasting templates from another category.","solutions":["Remove or update the extra `#template goal ...` line so only one goal remains per category","Combine the amounts into a single goal if both targets are intended"],"exampleFix":"// before\n#template goal 500\n#template goal 1000\n// after\n#template goal 1000","handlingStrategy":"validation","validationCode":"const goalLines = noteLines.filter(l => /#template\\s+goal/.test(l));\nif (goalLines.length > 1) throw new Error('only one #goal per category');","typeGuard":null,"tryCatchPattern":"try {\n  await init();\n} catch (e) {\n  if (e.message === 'Only one #goal is allowed per category') {\n    // keep one #template goal line and delete the rest\n  } else throw e;\n}","preventionTips":["Edit the existing goal line to change its amount instead of adding a new one","Grep category notes for `#template goal` when adding goals","Keep one goal directive per category per month"],"tags":["goal-templates","goal","duplicate","validation"],"backgroundTag":"duplicate-directive","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}