{"record":{"id":"327e687071c2a74f","repo":"JuliusBrussee/caveman","slug":"cave-reasoning-budget-exceeded","errorCode":"cave_reasoning_budget_exceeded","errorMessage":"cave_reasoning_budget_exceeded","messagePattern":"cave_reasoning_budget_exceeded","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":1590,"sourceCode":"          spendFailure ??= failure;\n        }\n      }\n      finalMessage = message;\n    };\n    const streamFn: StreamFn = async (selected, context, streamOptions) => {\n      if (options.signal?.aborted) {\n        throw options.signal.reason ?? new Error(\"cave_run_aborted\");\n      }\n      if (spendFailure) throw spendFailure;\n      if (usageFailure) throw usageFailure;\n      if (nestedUsage.incomplete) throw new Error(\"cave_nested_usage_incomplete\");\n      if (efficiencyPlan && reasoningUsageUnavailable) {\n        throw new Error(\"cave_reasoning_usage_unavailable\");\n      }\n      if (efficiencyPlan) {\n        enforceSemanticBudgets(contextBill(lowered.ir), outputTokens, efficiencyPlan);\n        if (reasoningTokens > efficiencyPlan.budgets.reasoning) {\n          throw new Error(\"cave_reasoning_budget_exceeded\");\n        }\n      }\n      // The hard model-call ceiling is a stop condition, not a failure: ending\n      // the run through the same graceful path as every other stop keeps the\n      // partial work and the receipt intact. Checked before the\n      // increment so exactly `maxModelCalls` calls are allowed.\n      if (modelCalls >= maxModelCalls) {\n        stopReason = \"call_budget_exhausted\";\n        refusalPending = true;\n        throw new Error(\"cave_run_stopped\");\n      }\n      modelCalls++;\n      // Between-calls stop point. Nothing is in flight here: the previous turn\n      // and its tools have finished and settled, and this call has not started.\n      const plan = () => decideNextCall({\n        meter: budgetMeter,\n        breakers,\n        deadlineAt,","sourceCodeStart":1572,"sourceCodeEnd":1608,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/runtime.ts#L1572-L1608","documentation":"Thrown before a model call when an efficiency plan is active and the run's accumulated reasoning tokens already exceed efficiencyPlan.budgets.reasoning. The check runs at each call boundary (enforceSemanticBudgets also guards context and output there), so a model that burned its reasoning allocation ends the run rather than silently exceeding the plan. Unlike the model-call ceiling, this is a real failure, not a graceful stop.","triggerScenarios":"candidatePlan/lockedBuild with budgets.reasoning: N, and the model's thinking tokens across the run surpass N; high-difficulty prompts that make the model reason heavily across several calls.","commonSituations":"Plans budgeted from easy eval prompts applied to harder production prompts; a locked build whose reasoning budget was tuned for a shorter context; models that increased default thinking effort after a version bump.","solutions":["Raise budgets.reasoning in the plan (or recompile the plan so budgets match actual reasoning demands) and re-lock","Reduce reasoning load: shorter histories, simpler decomposition, or a model/setting with lower thinking output","If the plan is only advisory for this run, run without candidatePlan/lockedBuild"],"exampleFix":"// before\nconst plan = { ..., budgets: { reasoning: 2048, ... } };\nawait agent.run(hardInput, { candidatePlan: plan });\n\n// after\nconst plan = { ..., budgets: { reasoning: 16384, ... } };\nawait agent.run(hardInput, { candidatePlan: plan });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await agent.run(input, { candidatePlan });\n} catch (e) {\n  if (e instanceof Error && e.message === \"cave_reasoning_budget_exceeded\") {\n    // recompile/re-lock with a larger budgets.reasoning, or split the task\n  } else throw e;\n}","preventionTips":["Budget reasoning headroom from production-difficulty prompts, not eval prompts","Watch reasoning token totals in receipts during load tests","Re-lock plans when model versions change thinking behavior"],"tags":["plans","reasoning","budget"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}