{"record":{"id":"1b62ddb46791b89b","repo":"JuliusBrussee/caveman","slug":"ledger-exceededcode","errorCode":null,"errorMessage":"${ledger.exceededCode}","messagePattern":"\\$\\{ledger\\.exceededCode\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":4671,"sourceCode":"): SpendReservation[] {\n  if (ledgers.length === 0) return [];\n  const outputTokens = Math.min(\n    model.maxTokens,\n    requestedOutputTokens ?? model.maxTokens,\n  );\n  const ceilingUsd = catalogSearchCeiling(\n    `${model.provider}/${model.id}`,\n    model.contextWindow,\n    outputTokens,\n  );\n  if (ceilingUsd === undefined) {\n    markSpendIncomplete(ledgers);\n    throw new Error(\"cave_subagent_unpriced_budget\");\n  }\n  for (const ledger of ledgers) {\n    if (ledger.incomplete) throw new Error(\"cave_subagent_spend_evidence_incomplete\");\n    if (ledger.actualUsd + ledger.reservedUsd + ceilingUsd > ledger.limitUsd) {\n      throw new Error(ledger.exceededCode);\n    }\n  }\n  const reservations = ledgers.map((ledger) => ({\n    ledger,\n    ceilingUsd,\n    provider: model.provider,\n    model: model.id,\n  }));\n  for (const { ledger } of reservations) ledger.reservedUsd += ceilingUsd;\n  return reservations;\n}\n\nfunction settleProviderSpend(\n  reservations: readonly SpendReservation[],\n  usage: ValidatedProviderUsage,\n): Error | undefined {\n  if (reservations.length === 0) return undefined;\n  const requested = reservations[0]!;","sourceCodeStart":4653,"sourceCodeEnd":4689,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/runtime.ts#L4653-L4689","documentation":"Pre-flight reservation for the next model call: for each active ledger, actualUsd + reservedUsd + ceilingUsd must fit under limitUsd, where ceilingUsd is the conservative worst-case cost of the call (byte-derived input ceiling times the catalog's worst rate plus the output allowance - deliberately ~3-4x the likely real cost). When the projection exceeds the limit, the ledger's exceededCode is thrown: 'cave_subagent_cost_budget' for subagent wallets (set at runtime.ts:3977) or 'cave_run_cost_budget_exceeded' for the root maxCostUsd ledger (runtime.ts:794). This is the older error-terminating cap; RunOptions.budget instead stops cleanly between calls with stopReason.","triggerScenarios":"The worst-case ceiling of the next call no longer fits the remaining wallet/root cap; large requestedOutputTokens inflating the ceiling past the limit; several subagents sharing one root maxCostUsd so reservations stack; late-run calls after most of the cap is already actual+reserved spend.","commonSituations":"maxCostUsd sized to expected average cost rather than worst-case reservation; raising requested output tokens mid-run; fan-out of budgeted subagents against a small root cap; conservative ceiling on huge contexts (byte bound ~3-4x tokens) tripping early.","solutions":["Raise the limit (maxCostUsd / subagent cap) so the worst-case ceiling fits with headroom","Lower requestedOutputTokens for the call so the ceiling shrinks","Prefer RunOptions.budget (reserve-and-clamp): it clamps output to what remains and stops between calls with stopReason instead of throwing","Reduce the number of parallel budgeted subagents sharing one cap"],"exampleFix":"// before: error-terminating cap sized too tight for worst-case\nawait run(agent, { maxCostUsd: 1, requestedOutputTokens: 32_000 });\n\n// after: clamping budget, or bigger cap for worst case\nawait run(agent, { budget: { denomination: 'usd', maxUsd: 1 } }); // clamps + stopReason: budget_exhausted","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await run(agent, opts);\n} catch (error) {\n  const msg = error instanceof Error ? error.message : '';\n  if (msg === 'cave_subagent_cost_budget' || msg === 'cave_run_cost_budget_exceeded') {\n    // worst-case reservation no longer fits: raise the cap or lower output tokens\n    return await run(agent, { ...opts, maxCostUsd: opts.maxCostUsd! * 2 });\n  }\n  throw error;\n}","preventionTips":["Size maxCostUsd for the worst-case reservation (byte-derived ceiling ~3-4x real token cost), not the average","Prefer RunOptions.budget (reserve-and-clamp, stops with stopReason) over the error-throwing maxCostUsd cap","Keep requestedOutputTokens modest so per-call ceilings fit the remaining cap","Limit how many budgeted subagents share one root cap per turn"],"tags":["budget","cost","reservation","worst-case-ceiling"],"backgroundTag":"cost-budget-exceeded","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}