{"record":{"id":"598de69b10304dfb","repo":"actualbudget/actual","slug":"only-one-up-to-allowed-per-category","errorCode":null,"errorMessage":"Only one `up to` allowed per category","messagePattern":"Only one `up to` allowed per category","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/budget/category-template-context.ts","lineNumber":588,"sourceCode":"      t =>\n        t.type === 'simple' ||\n        t.type === 'periodic' ||\n        t.type === 'limit' ||\n        t.type === 'remainder',\n    )) {\n      let limitDef;\n      if (template.type === 'limit') {\n        limitDef = template;\n      } else {\n        if (template.limit) {\n          limitDef = template.limit;\n        } else {\n          continue; // may not have a limit defined in the template\n        }\n      }\n\n      if (this.limitCheck) {\n        throw new Error('Only one `up to` allowed per category');\n      }\n\n      if (limitDef.period === 'daily') {\n        const numDays = monthUtils.differenceInCalendarDays(\n          monthUtils.addMonths(this.month, 1),\n          this.month,\n        );\n        this.limitAmount +=\n          amountToInteger(limitDef.amount, this.currency.decimalPlaces) *\n          numDays;\n      } else if (limitDef.period === 'weekly') {\n        if (!limitDef.start) {\n          throw new Error('Weekly limit requires a start date (YYYY-MM-DD)');\n        }\n        const nextMonth = monthUtils.nextMonth(this.month);\n        let week = limitDef.start;\n        const baseLimit = amountToInteger(\n          limitDef.amount,","sourceCodeStart":570,"sourceCodeEnd":606,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/budget/category-template-context.ts#L570-L606","documentation":"A category may have at most one `up to` (limit) clause across its templates. `checkLimit` runs in the CategoryTemplateContext constructor and throws when a second limit directive is found after one was already accepted (`this.limitCheck` is true).","triggerScenarios":"Two or more template lines for the same category both define an `up to <amount> ...` limit — e.g. `#template up to 100` plus `#template 50 up to 200` in the same category's note in the same month.","commonSituations":"Adding a new limit template without removing the old one; merging template lines from two sources; a leftover `up to` from a previous month's note that is still active.","solutions":["Remove or comment out one of the `up to` templates so only a single limit remains per category","Combine the intent into one template line with a single `up to` clause"],"exampleFix":"// before\n#template up to 100\n#template 50 up to 200\n// after\n#template 50 up to 200","handlingStrategy":"validation","validationCode":"const upToLines = noteLines.filter(l => /\\bup to\\b/.test(l));\nif (upToLines.length > 1) throw new Error('at most one `up to` clause per category');","typeGuard":null,"tryCatchPattern":"try {\n  await init();\n} catch (e) {\n  if (e.message.includes(\"Only one `up to` allowed\")) {\n    // remove/merge all but one up-to line in the category note\n  } else throw e;\n}","preventionTips":["Replace (not append) limit lines when changing amounts","Search the category note for `up to` before adding a new limit","Keep one canonical limit directive per category"],"tags":["goal-templates","limit","duplicate","validation"],"backgroundTag":"duplicate-directive","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}