JuliusBrussee/caveman · error
cave_subagent_call_budget
cave_subagent_call_budget
Error message
cave_subagent_call_budget
What it means
Each subagent tool name has a per-execution call budget tracked in usage.calls; before dispatching, the runtime compares the current call count against runtime.maxCalls and throws cave_subagent_call_budget when it has already been reached. The counter is incremented synchronously right after the check so parallel dispatch cannot double-spend the budget.
Solutions
- Raise runtime.maxCalls for that subagent tool in the configuration
- Restructure the prompt/workflow so fewer subagent invocations are needed (batch work into one call)
- Use a differently-named subagent tool for additional budget
- Cache subagent results and reuse them instead of re-dispatching
Example fix
// before
runtime: { kind: "subagent", maxCalls: 3 }
// after
runtime: { kind: "subagent", maxCalls: 10 } Defensive patterns
Strategy: validation
Validate before calling
const used = getUsage().calls.get(subagentToolName) ?? 0;
if (used >= subagentRuntime.maxCalls) {
// reuse cached result or skip dispatch
} Try / catch
try {
await dispatchSubagent({ task });
} catch (e) {
if (e.message === "cave_subagent_call_budget") {
result = getCachedSubagentResult(subagentToolName) ?? synthesizeWithoutSubagent();
} else throw e;
} Prevention
- Budget maxCalls above the expected number of dispatches per run
- Cache subagent outputs for repeated identical work
- Avoid retry loops that re-dispatch after transient failures without counting them
- Alert when usage.calls approaches maxCalls mid-run
When it happens
Trigger: Invoking the same subagent tool more than runtime.maxCalls times within one parent agent execution (per usage.calls ledger) — e.g. a maxCalls: 3 budget hit on the 4th call.
Common situations: Looping agent that keeps re-dispatching the same researcher subagent; maxCalls configured too low for the task; retried tool calls consuming the budget after partial failures.
Related errors
- cave_subagent_concurrency_limit
- cave_subagent_context_budget
- cave_subagent_cost_budget
- cave_subagent_depth_limit
- cave_subagent_input_limit
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/5ffb2a7c341dca5c.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/runtime.ts:3874
toolDefinition: ToolDefinition,
params: unknown,
signal: AbortSignal | undefined,
parentOptions: InternalRunOptions,
usage: NestedUsage,
executionContext: InternalExecutionContext,
parentMeter: BudgetMeter | undefined,
parentDeadlineAt: number | undefined,
): Promise<unknown> {
const runtime = toolDefinition.runtime;
if (runtime?.kind !== "subagent") throw new Error("cave_subagent_runtime_missing");
if (params === null || typeof params !== "object" || Array.isArray(params) ||
typeof (params as { task?: unknown }).task !== "string") {
throw new Error("cave_subagent_arguments_invalid");
}
const task = (params as { task: string }).task;
if (task.length > runtime.maxInputChars) throw new Error("cave_subagent_input_limit");
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");
}View on GitHub (pinned to 3ee70a1026)