{"record":{"id":"ca17a6279d776e9d","repo":"shareAI-lab/learn-claude-code","slug":"token-budget-exceeded-self-spent-n-self","errorCode":null,"errorMessage":"token budget exceeded ({self._spent + n} > {self._total})","messagePattern":"token budget exceeded \\((.+?) > (.+?)\\)","errorType":"validation","errorClass":"WorkflowInputError","httpStatus":null,"severity":"error","filePath":"s16_workflow_runtime/code.py","lineNumber":381,"sourceCode":"        self._f.flush()\n        self.cache[key] = value\n\n    def close(self):\n        self._f.close()\n\n\n# -- Token Budget --\nclass Budget:\n    \"\"\"budget.total / spent() / remaining(). Once spent reaches total, agent()\n    calls raise instead of silently overspending.\"\"\"\n\n    def __init__(self, total=None):\n        self.total = total\n        self._spent = 0\n\n    def add(self, n):\n        if self.total is not None and self._spent + n > self.total:\n            raise WorkflowInputError(\n                f\"token budget exceeded ({self._spent + n} > {self.total})\"\n            )\n        self._spent += n\n\n    def spent(self):\n        return self._spent\n\n    def remaining(self):\n        return float(\"inf\") if self.total is None else max(0, self.total - self._spent)\n\n\n# -- Workflow Task Lifecycle --\nclass LocalWorkflowTask:\n    \"\"\"Hold workflow status, usage, and progress events.\"\"\"\n\n    def __init__(self, task_id, run_id, meta):\n        self.task_id = task_id\n        self.run_id = run_id","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s16_workflow_runtime/code.py#L363-L399","documentation":"Budget.add raises WorkflowInputError when adding n tokens would push spent above total. The budget exists so runaway workflows fail loudly at the next agent() call instead of silently overspending; the message shows the exact arithmetic (spent + n > total). With total=None the budget is unlimited and this error cannot fire.","triggerScenarios":"Launching with run(..., budget_total=N) and the workflow's cumulative agent usage reaching N — the agent() call whose cost tips over the limit raises. Large schemas/prompts with per-call costs comparable to the whole budget. Fan-out workflows whose parallel agent() calls each add to the same Budget.","commonSituations":"Iterating on a workflow with a small test budget left in place for production. Model/pricing changes making per-call token counts larger than when the budget was sized. Loops with weak termination conditions consuming budget steadily.","solutions":["Raise or remove the budget: run(..., budget_total=None) for unlimited, or size it to expected agents x mean cost","Check budget.remaining() before expensive agent() calls and exit gracefully","Trim prompt/schema sizes if a single call approaches the total"],"exampleFix":"# before\nrun(workflow, meta=meta, budget_total=5000)  # too small for the fan-out\n\n# after\nrun(workflow, meta=meta, budget_total=200_000)","handlingStrategy":"validation","validationCode":"# before each expensive call inside the workflow:\nif state.budget.remaining() < ESTIMATED_TOKENS_PER_CALL:\n    state.task.update(\"halted: token budget nearly exhausted\")\n    return  # exit gracefully instead of letting Budget.add raise","typeGuard":null,"tryCatchPattern":"try:\n    result = state.agent(prompt, schema=schema)\nexcept WorkflowInputError as exc:\n    if \"token budget exceeded\" not in str(exc):\n        raise\n    state.task.update(\"stopped: token budget exceeded\")\n    return  # or re-run with a raised budget_total after operator approval","preventionTips":["Size budget_total from measured mean cost x planned agent calls, plus headroom","Check remaining() before large calls; fail gracefully near the limit","Remove small test budgets before production runs"],"tags":["workflow","budget","tokens","limits","cost"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}