{"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":"error_code","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/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/runtime.ts#L4044-L4080","documentation":"When a spend ledger is configured, the runtime checks after each child run that the ledger is complete and that actual spend has not exceeded runtime.maxCostUsd. If the ledger is marked incomplete (unverifiable evidence) or actualUsd > maxCostUsd, usage is marked incomplete and this error is thrown. It is the cost-budget counterpart of the context-token budget check.","triggerScenarios":"Spawning a nested subagent while a spendLedger is attached and either spendLedger.incomplete === true, or spendLedger.actualUsd exceeds runtime.maxCostUsd after the child's costUsd was added (runtime.ts:4062).","commonSituations":"maxCostUsd set too low for the delegated workload; multiple subagent runs accumulating spend on one ledger; pricing changed so accumulated actualUsd crossed the limit mid-run; ledger marked incomplete by an earlier unpriced-model event.","solutions":["Raise runtime.maxCostUsd to accommodate the expected total spend of the parent plus children.","Inspect spendLedger.actualUsd to see how much was spent and which child runs consumed it; reduce delegation depth or frequency.","If the ledger is incomplete, fix the underlying unpriced/missing-evidence condition before re-running.","Pre-check remaining budget before spawning each subagent and skip delegation when close to the cap."],"exampleFix":"// before\nconst runtime = createRuntime({ maxCostUsd: 0.05 });\n// after\nconst runtime = createRuntime({ maxCostUsd: 1.0 });","handlingStrategy":"validation","validationCode":"if (spendLedger && (spendLedger.incomplete || spendLedger.actualUsd > runtime.maxCostUsd)) {\n  throw new Error('cost budget already exhausted; skipping delegation');\n}","typeGuard":"const budgetHealthy = (ledger) =>\n  ledger !== undefined && !ledger.incomplete && ledger.actualUsd <= ledger.limitUsd;","tryCatchPattern":"try {\n  await spawnSubagent(task);\n} catch (e) {\n  if (e.message === 'cave_subagent_cost_budget') {\n    escalateForBudgetApproval();\n  } else throw e;\n}","preventionTips":["Track spendLedger.actualUsd before each delegation and stop near the cap.","Size maxCostUsd from measured per-run costs, not guesses.","Fix incomplete ledgers promptly; they cascade into later runs."],"tags":["budget","cost","subagent","spend-ledger"],"backgroundTag":"value-out-of-range","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}