{"record":{"id":"287a63281f2890e4","repo":"JuliusBrussee/caveman","slug":"cave-subagent-cost-budget","errorCode":"cave_subagent_cost_budget","errorMessage":"cave_subagent_cost_budget","messagePattern":"cave_subagent_cost_budget","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":4062,"sourceCode":"    throw new Error(\"cave_nested_usage_incomplete\");\n  }\n  usage.inputTokens += child.inputTokens;\n  usage.outputTokens += child.outputTokens;\n  usage.cacheReadTokens += child.cacheReadTokens;\n  usage.cacheWriteTokens += child.cacheWriteTokens;\n  usage.reasoningTokens += child.reasoningTokens;\n  usage.costUsd += child.costUsd;\n  usage.receipts.push(child.receipt);\n  if (child.mode === \"observe-only\") usage.observeOnly = true;\n  if (child.priceBasis !== \"public_catalog\") usage.unpriced = true;\n  if (child.inputTokens > runtime.maxContextTokens) {\n    usage.incomplete = true;\n    throw new Error(\"cave_subagent_context_budget\");\n  }\n  if (spendLedger !== undefined &&\n      (spendLedger.incomplete || spendLedger.actualUsd > runtime.maxCostUsd)) {\n    usage.incomplete = true;\n    throw new Error(\"cave_subagent_cost_budget\");\n  }\n  return {\n    text: child.text,\n    agent_id: child.agentId,\n    usage_basis: child.usageBasis,\n    input_tokens: child.inputTokens,\n    output_tokens: child.outputTokens,\n    cost_usd: child.costUsd,\n    // A child that ran out of wallet returns partial work; the caller sees why\n    // rather than reading a truncated answer as a complete one.\n    stop_reason: child.stopReason,\n    claim_basis: \"inferred\",\n  };\n}\n\nfunction completeUsage(result: RunResult): boolean {\n  if (result.reasoningUsageBasis !== \"provider_reported\") return false;\n  return [","sourceCodeStart":4044,"sourceCodeEnd":4080,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/runtime.ts#L4044-L4080","documentation":"After the child settles, the tool's SpendLedger is re-checked: if it is flagged incomplete, or its actualUsd (catalog-priced dollars really spent by the child and its descendants) exceeds runtime.maxCostUsd, the parent aborts. The child exceeded its wallet, or an unpriced call inside the child poisoned the ledger; either way the cap is enforced terminally rather than silently exceeded.","triggerScenarios":"Child's real catalog-priced spend exceeded its maxCostUsd wallet (wallet sized exactly at expected cost with no headroom); a model inside the child's graph absent from the catalog marked the ledger incomplete, tripping the incomplete clause; descendants of the child spending against the same carved wallet.","commonSituations":"Wallets sized to the point estimate of a task whose cost varies (long research loops); retry-heavy children; unpriced regional models inside a budgeted child; maxCostUsd inherited from parent config without rescaling.","solutions":["Raise the subagent's maxCostUsd with headroom (the carve at spawn must still fit the parent's remaining budget)","Shrink the child's task or limit its iterations/tool calls so actual spend stays under the wallet","Ensure every model in the child's graph is catalog-priced to avoid the incomplete-ledger path","Inspect the child receipt (usage.receipts) to see where the dollars went before resizing"],"exampleFix":"// before\nconst child = parent.subagent({ maxCostUsd: 0.5 }); // child burns 0.62 -> throw\n\n// after\nconst child = parent.subagent({ maxCostUsd: 1.5 });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await dispatchSubagent(task);\n} catch (error) {\n  if (error instanceof Error && error.message === 'cave_subagent_cost_budget') {\n    // wallet exhausted mid-run: surface partial child work if captured, then stop this branch\n    return reportWalletExhausted(toolName);\n  }\n  throw error;\n}","preventionTips":["Size subagent maxCostUsd wallets with 2-3x headroom over the expected catalog-priced cost","Keep the wallet carve within the parent's remaining budget when raising it","Ensure every model in the child graph is priced so the incomplete-ledger clause cannot fire","Inspect child receipts after each run and resize wallets before the next run"],"tags":["subagent","budget","cost","wallet"],"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"}