{"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":"error_code","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/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/runtime.ts#L3873-L3909","documentation":"When a parent has a BudgetMeter, the child's wallet must be carved from it in the parent's denomination: maxCostUsd for \"usd\" meters or maxTokens for token meters. If the meter's denomination has no corresponding runtime limit configured (walletAmount is undefined), the runtime refuses to fund the subagent and throws this error, since it cannot determine how much budget to carve.","triggerScenarios":"Parent agent runs under a token-denominated BudgetMeter but the subagent runtime defines only maxCostUsd (or vice versa: usd meter with only maxTokens set), so walletAmount is undefined while parentMeter is defined.","commonSituations":"Mixed-budget setups: budgets switched from tokens to USD (or the reverse) without updating each subagent's maxCostUsd/maxTokens; a shared subagent config used under both denominations; defaults omitting the field matching the meter's denomination.","solutions":["Set the runtime field matching the parent meter's denomination: maxCostUsd when the meter is \"usd\", maxTokens otherwise","Run the parent without a budget (parentMeter undefined) if subagents should be unfunded — then no carve is required","Define both maxCostUsd and maxTokens on the subagent runtime so either denomination works","Align budget denomination across parent and subagent configurations"],"exampleFix":"// before (parent meter is \"usd\")\nruntime: { kind: \"subagent\", maxTokens: 50000 }\n// after\nruntime: { kind: \"subagent\", maxCostUsd: 0.5, maxTokens: 50000 }","handlingStrategy":"validation","validationCode":"if (parentMeter !== undefined) {\n  const need = parentMeter.denomination === \"usd\" ? runtime.maxCostUsd : runtime.maxTokens;\n  if (need === undefined) {\n    throw new Error(\"subagent lacks a \" + parentMeter.denomination + \" budget\");\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await dispatchSubagent({ task });\n} catch (e) {\n  if (e.message === \"cave_subagent_wallet_denomination_unavailable\") {\n    // run the child unfunded or fix the config, then retry\n    await dispatchSubagent({ task, ignoreBudget: true });\n  } else throw e;\n}","preventionTips":["Always define both maxCostUsd and maxTokens on subagent runtimes","Keep parent budget denomination and subagent limits in sync in config","Add a config lint that checks denomination coverage for every subagent","Document which denomination each budget field applies to"],"tags":["subagent","budget","denomination","configuration"],"backgroundTag":"missing-required-config-field","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"}