{"record":{"id":"8f4a7a497019642e","repo":"JuliusBrussee/caveman","slug":"cave-subagent-wallet-denomination-unavailable","errorCode":"cave_subagent_wallet_denomination_unavailable","errorMessage":"cave_subagent_wallet_denomination_unavailable","messagePattern":"cave_subagent_wallet_denomination_unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":3891,"sourceCode":"  const calls = usage.calls.get(toolDefinition.name) ?? 0;\n  if (calls >= runtime.maxCalls) throw new Error(\"cave_subagent_call_budget\");\n  // Reserve synchronously before any await so parallel Pi tool dispatch cannot\n  // pass the same maxCalls check twice.\n  usage.calls.set(toolDefinition.name, calls + 1);\n  const depth = executionContext.depth;\n  const depthLimit = Math.min(\n    parentOptions.maxSubagentDepth ?? DEFAULT_SUBAGENT_DEPTH_LIMIT,\n    ABSOLUTE_SUBAGENT_DEPTH_LIMIT,\n  );\n  if (depth + 1 > depthLimit) throw new Error(\"cave_subagent_depth_limit\");\n  // The wallet is carved here, still synchronously, for the same reason: two\n  // subagents dispatched in one turn must not both be funded out of the same\n  // remaining budget.\n  const walletAmount = parentMeter === undefined\n    ? undefined\n    : parentMeter.denomination === \"usd\" ? runtime.maxCostUsd : runtime.maxTokens;\n  if (parentMeter !== undefined && walletAmount === undefined) {\n    throw new Error(\"cave_subagent_wallet_denomination_unavailable\");\n  }\n  const carve = parentMeter === undefined || walletAmount === undefined\n    ? undefined\n    : parentMeter.carve(walletAmount);\n  if (parentMeter !== undefined && carve === undefined) {\n    throw new Error(\"cave_subagent_wallet_unavailable\");\n  }\n  let releaseAdmission: (() => void) | undefined;\n  try {\n    releaseAdmission = admitSubagent(executionContext.invocationState);\n    return await runSubagent({\n      toolDefinition,\n      runtime,\n      task,\n      signal,\n      parentOptions,\n      usage,\n      executionContext,","sourceCodeStart":3873,"sourceCodeEnd":3909,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/runtime.ts#L3873-L3909","documentation":"When the parent run carries a BudgetMeter, each subagent dispatch must be funded from a wallet carved out of it: maxCostUsd for a usd-denominated meter, maxTokens otherwise. If the subagent runtime definition lacks the field matching the meter's denomination, walletAmount is undefined and the run aborts with cave_subagent_wallet_denomination_unavailable before the subagent starts.","triggerScenarios":"Parent meter denomination \"usd\" but the subagent runtime defines only maxTokens (no maxCostUsd); a token-denominated meter with a runtime that defines only maxCostUsd. Any dispatch while the meter is present fails until the field exists.","commonSituations":"Subagent definitions copy-pasted from a setup billed in the other denomination; switching the parent meter between token and usd modes without updating subagent runtimes; test fixtures defining only one of the two caps.","solutions":["Define both maxCostUsd and maxTokens on the subagent runtime so any meter denomination can fund it","Or align the parent meter's denomination with the single field your subagent runtimes define"],"exampleFix":"// before — parent meter denomination is \"usd\"\nruntime: { kind: \"subagent\", maxTokens: 50_000, /* no maxCostUsd */ }\n\n// after\nruntime: { kind: \"subagent\", maxTokens: 50_000, maxCostUsd: 0.5 }","handlingStrategy":"validation","validationCode":"if (parentMeter !== undefined) {\n  const requiredField = parentMeter.denomination === \"usd\" ? \"maxCostUsd\" : \"maxTokens\";\n  if (tool.runtime[requiredField] === undefined) {\n    throw new Error(`subagent ${tool.name} must define ${requiredField} for a ${parentMeter.denomination}-denominated meter`);\n  }\n}","typeGuard":"function canFundSubagent(\n  runtime: { maxCostUsd?: number; maxTokens?: number },\n  meter: { denomination: string },\n): boolean {\n  return meter.denomination === \"usd\"\n    ? runtime.maxCostUsd !== undefined\n    : runtime.maxTokens !== undefined;\n}","tryCatchPattern":null,"preventionTips":["Define both maxCostUsd and maxTokens on every subagent runtime definition","When switching meter denomination, audit all subagent runtimes in the same change","Validate subagent tool definitions against the parent meter at startup"],"tags":["subagents","budget","configuration"],"backgroundTag":"budget-denomination-mismatch","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}