JuliusBrussee/caveman · error
cave_subagent_wallet_denomination_unavailable
cave_subagent_wallet_denomination_unavailable
Error message
cave_subagent_wallet_denomination_unavailable
What it means
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.
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
Example fix
// before (parent meter is "usd")
runtime: { kind: "subagent", maxTokens: 50000 }
// after
runtime: { kind: "subagent", maxCostUsd: 0.5, maxTokens: 50000 } Defensive patterns
Strategy: validation
Validate before calling
if (parentMeter !== undefined) {
const need = parentMeter.denomination === "usd" ? runtime.maxCostUsd : runtime.maxTokens;
if (need === undefined) {
throw new Error("subagent lacks a " + parentMeter.denomination + " budget");
}
} Try / catch
try {
await dispatchSubagent({ task });
} catch (e) {
if (e.message === "cave_subagent_wallet_denomination_unavailable") {
// run the child unfunded or fix the config, then retry
await dispatchSubagent({ task, ignoreBudget: true });
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- budget is below CCR storage minimum
- cave_subagent_call_budget
- cave_subagent_cost_budget
- cave_subagent_runtime_missing
- cave_subagent_unpriced_budget
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/8f4a7a497019642e.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/runtime.ts:3891
const calls = usage.calls.get(toolDefinition.name) ?? 0;
if (calls >= runtime.maxCalls) throw new Error("cave_subagent_call_budget");
// Reserve synchronously before any await so parallel Pi tool dispatch cannot
// pass the same maxCalls check twice.
usage.calls.set(toolDefinition.name, calls + 1);
const depth = executionContext.depth;
const depthLimit = Math.min(
parentOptions.maxSubagentDepth ?? DEFAULT_SUBAGENT_DEPTH_LIMIT,
ABSOLUTE_SUBAGENT_DEPTH_LIMIT,
);
if (depth + 1 > depthLimit) throw new Error("cave_subagent_depth_limit");
// The wallet is carved here, still synchronously, for the same reason: two
// subagents dispatched in one turn must not both be funded out of the same
// remaining budget.
const walletAmount = parentMeter === undefined
? undefined
: parentMeter.denomination === "usd" ? runtime.maxCostUsd : runtime.maxTokens;
if (parentMeter !== undefined && walletAmount === undefined) {
throw new Error("cave_subagent_wallet_denomination_unavailable");
}
const carve = parentMeter === undefined || walletAmount === undefined
? undefined
: parentMeter.carve(walletAmount);
if (parentMeter !== undefined && carve === undefined) {
throw new Error("cave_subagent_wallet_unavailable");
}
let releaseAdmission: (() => void) | undefined;
try {
releaseAdmission = admitSubagent(executionContext.invocationState);
return await runSubagent({
toolDefinition,
runtime,
task,
signal,
parentOptions,
usage,
executionContext,View on GitHub (pinned to 3ee70a1026)