JuliusBrussee/caveman · error
cave_subagent_wallet_unavailable
cave_subagent_wallet_unavailable
Error message
cave_subagent_wallet_unavailable
What it means
A subagent whose runtime declares a spend cap (maxCostUsd or maxTokens) must have that entire cap carved out of the parent's remaining budget as a wallet, synchronously at spawn, so parallel children cannot double-spend the same remainder. carve() (budget.ts:376) returns undefined when the parent meter is breached/revoked, when the amount is not finite/positive (or not a safe integer in token denomination), or when the amount exceeds the parent's remaining budget. This error means the parent cannot fund the child's wallet, so dispatch aborts before the child is admitted.
Source
Thrown at packages/agent/src/runtime.ts:3897
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,
depth,
childMeter: carve?.child,
parentDeadlineAt,
});
} finally {
releaseAdmission?.();View on GitHub (pinned to 766dce6b13)
Solutions
- Lower the subagent's maxCostUsd/maxTokens below the parent's remaining budget (check the run receipt's spend/tranche history to see what remains)
- Dispatch fewer subagents per turn, or size them so the sum of caps fits the remainder
- Top up the parent budget via createBudgetController() checkpoints (release() is max-bounded) before dispatching more children
- Align denominations: parent budget maxUsd pairs with child maxCostUsd, maxTokens pairs with integer maxTokens
- Do not retry dispatch after a capBreached parent; inspect RunResult.capBreached/stopReason first
Example fix
// before: child cap equals parent budget already partly spent
const parent = agent({ ... });
const child = parent.subagent({ maxCostUsd: 5 }); // parent already spent $3 of $5
// after: size wallet against remaining budget with headroom
const remaining = 5 - spentUsdFromReceipt;
const child = parent.subagent({ maxCostUsd: Math.max(0.5, remaining * 0.8) }); Defensive patterns
Strategy: try-catch
Validate before calling
// Before dispatching budgeted subagents, size the wallet against remaining parent budget
const parentBudgetUsd = 5;
const spentUsd = sumSpentFromReceipt(rootReceipt); // roll up receipts of finished runs
const remaining = parentBudgetUsd - spentUsd;
const wallet = Math.min(subagentCap, remaining * 0.8); // headroom
if (wallet <= 0) throw new Error('no parent budget left for subagent wallet'); Try / catch
try {
const out = await child(task);
} catch (error) {
if (error instanceof Error && error.message === 'cave_subagent_wallet_unavailable') {
// parent meter cannot fund this cap: shrink the wallet or stop dispatching
logger.warn('subagent wallet unfundable; remaining parent budget too small');
return fallbackWithoutSubagent();
}
throw error;
} Prevention
- Size every subagent cap below the parent's remaining budget, never equal to the full parent budget
- Sum caps of subagents dispatched in one turn and keep the total under the parent remainder (carves are synchronous and stacking)
- Match denominations: parent maxUsd budget with child maxCostUsd, token budget with integer maxTokens
- Check RunResult.capBreached/stopReason before dispatching further children
When it happens
Trigger: Parent run started with RunOptions.budget (or maxCostUsd) and a subagent() declares maxCostUsd/maxTokens greater than the parent's remaining budget; dispatching two subagents in one turn whose combined caps exceed the remainder (the first carve consumes it); parent meter already capBreached or revoked; parent budget in tokens while the child's maxTokens is fractional/non-integer.
Common situations: Giving the child the same cap as the parent after the parent already spent on earlier calls; sizing wallets with zero headroom then adding one more parallel subagent; mixing denominations between parent budget and child cap; retrying a subagent dispatch after the parent budget was nearly drained.
Related errors
- cave_subagent_cost_budget
- caveman agent: subagent maxCostUsd must be positive
- caveman agent: subagent maxTokens must be a positive integer
- cave_subagent_unpriced_budget
- cave_breaker_retry_requires_budget
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/4135bea9d9548534.
Report an issue: GitHub.