JuliusBrussee/caveman · error
cave_subagent_wallet_denomination_unavailable
cave_subagent_wallet_denomination_unavailable
Error message
cave_subagent_wallet_denomination_unavailable
What it means
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.
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 766dce6b13)
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
Example fix
// before — parent meter denomination is "usd"
runtime: { kind: "subagent", maxTokens: 50_000, /* no maxCostUsd */ }
// after
runtime: { kind: "subagent", maxTokens: 50_000, maxCostUsd: 0.5 } Defensive patterns
Strategy: validation
Validate before calling
if (parentMeter !== undefined) {
const requiredField = parentMeter.denomination === "usd" ? "maxCostUsd" : "maxTokens";
if (tool.runtime[requiredField] === undefined) {
throw new Error(`subagent ${tool.name} must define ${requiredField} for a ${parentMeter.denomination}-denominated meter`);
}
} Type guard
function canFundSubagent(
runtime: { maxCostUsd?: number; maxTokens?: number },
meter: { denomination: string },
): boolean {
return meter.denomination === "usd"
? runtime.maxCostUsd !== undefined
: runtime.maxTokens !== undefined;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- cave_budget_denomination_ambiguous
- cave_budget_max_invalid
- cave_budget_on_exhausted_invalid
- caveman agent: subagent maxCostUsd must be positive
- cave_budget_conflicting_cap
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/8f4a7a497019642e.
Report an issue: GitHub.