JuliusBrussee/caveman · error
cave_nested_usage_incomplete
cave_nested_usage_incomplete
Error message
cave_nested_usage_incomplete
What it means
Thrown inside the per-call streamFn guard before any new model call: a nested subagent invocation reported incomplete usage (nestedUsage.incomplete), so the ancestor budget ledgers can no longer be trusted to account the next call. The runtime fails closed instead of continuing with a hole in the spend record. This is a hard accounting integrity error, not a transient provider hiccup you can retry past.
Source
Thrown at packages/agent/src/runtime.ts:1583
budgetReservation.amount,
"unavailable_worst_case",
);
}
}
if (reservation !== undefined && reservation.length > 0) {
markSpendIncomplete(reservation.map((item) => item.ledger));
spendFailure ??= failure;
}
}
finalMessage = message;
};
const streamFn: StreamFn = async (selected, context, streamOptions) => {
if (options.signal?.aborted) {
throw options.signal.reason ?? new Error("cave_run_aborted");
}
if (spendFailure) throw spendFailure;
if (usageFailure) throw usageFailure;
if (nestedUsage.incomplete) throw new Error("cave_nested_usage_incomplete");
if (efficiencyPlan && reasoningUsageUnavailable) {
throw new Error("cave_reasoning_usage_unavailable");
}
if (efficiencyPlan) {
enforceSemanticBudgets(contextBill(lowered.ir), outputTokens, efficiencyPlan);
if (reasoningTokens > efficiencyPlan.budgets.reasoning) {
throw new Error("cave_reasoning_budget_exceeded");
}
}
// The hard model-call ceiling is a stop condition, not a failure: ending
// the run through the same graceful path as every other stop keeps the
// partial work and the receipt intact. Checked before the
// increment so exactly `maxModelCalls` calls are allowed.
if (modelCalls >= maxModelCalls) {
stopReason = "call_budget_exhausted";
refusalPending = true;
throw new Error("cave_run_stopped");
}View on GitHub (pinned to 27d5a3981a)
Solutions
- Configure subagents with providers/models that always report complete token usage in every response
- If a custom streamFn is used for child runs, make sure it propagates provider usage rather than discarding it
- Isolate the offending subagent: run it as its own top-level agent (its incomplete usage then cannot poison a parent ledger) and inspect its receipt
- Check the child's receipt to find which call reported incomplete usage, then fix that provider configuration
Defensive patterns
Strategy: try-catch
Try / catch
try {
await agent.run(input, opts);
} catch (e) {
if (e instanceof Error && e.message === "cave_nested_usage_incomplete") {
// Inspect child receipts to find the subagent whose usage is missing.
// Do not retry the same configuration: fail the task and fix the child provider.
} else throw e;
} Prevention
- Use subagent providers that report complete usage on every turn
- Never strip usage when wrapping child transports with a custom streamFn
- Smoke-test each subagent standalone and check its receipt for complete usage before nesting it
When it happens
Trigger: A subagent tool's provider or transport failed to report complete usage for its turns (missing/zero usage in provider responses), and then the parent attempts its next model call. The flag is checked at every subsequent call, so the run halts on the first parent call after the incomplete child settles.
Common situations: A nested agent using a provider or custom model that omits usage fields in its responses; a subagent routed through a transport (streamFn/proxy) that drops usage; each reserved turn requires complete usage, so any child that cannot report it poisons the parent run.
Related errors
- cave_retry_accounting_invalid
- cave_budget_reservation_double_settle
- cave_budget_denomination_unavailable
- cave_claude_reasoning_capability_unknown:${model}
- unknown command ${JSON.stringify(command)}; run caveman-agen
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/e4d7598df2270ee3.
Report an issue: GitHub.