Hmbown/CodeWhale · error · anyhow::Error
Sub-agent token budget exhausted for scope
Error message
Sub-agent token budget exhausted for scope {scope_id}: {spent}/{limit} tokens spent, {remaining} remaining (including reserved reporting allowance). Wait for the parent/Workflow to summarize results or start a fresh agent run. What it means
Thrown by `resolve_budget_scope` when the token budget for a scope is effectively exhausted: after subtracting already-spent tokens, the reserved reporting/handback allowance, and reserved handback tokens, less than `MIN_SUBAGENT_SPAWN_TOKEN_RESERVE` remains — so no new sub-agent work can be funded. It deliberately tells the caller to stop spawning and let the parent summarize instead of continuing to burn the budget.
Solutions
- Let the parent/Workflow summarize the results already produced and end the run — this is the error's own recommendation.
- Start a fresh agent run (new session or new scope id) with a reset budget.
- Raise the token limit for the scope if the workload legitimately needs more tokens.
- Audit which sub-agents spent the budget (via the budget accounting) and reduce per-child allowances or task sizes.
Defensive patterns
Strategy: try-catch
Validate before calling
let spent = engine.aggregate_budget_spent(&scope_id);
if limit.saturating_sub(spent) < MIN_WORK_TOKENS { /* summarize or restart before spawning */ } Try / catch
match engine.spawn_sub_agent(scope_id, task) {
Err(e) if e.to_string().contains("token budget exhausted") => {
let summary = parent_summarize_results();
start_fresh_scope_run(summary);
}
other => other?,
} Prevention
- Track spent vs limit per scope and stop spawning before the reserve threshold is hit.
- Size the budget limit to the expected workload; prefer several smaller scopes over one giant scope.
- Have the parent summarize child results incrementally instead of queueing ever more sub-agent work.
- Log budget consumption per child so expensive runs are visible early.
When it happens
Trigger: Attempting to spawn/resume sub-agent work under scope `scope_id` when `aggregate_budget_spent(scope_id)` plus reserves leave `work_remaining < MIN_SUBAGENT_SPAWN_TOKEN_RESERVE` (mod.rs:5255). Triggered by large model responses, many child runs under one scope, or a limit set too low for the workload.
Common situations: Long-running Workflow runs where children consumed the whole token budget; a parent orchestrator spawning many sub-agents under a single scope id; misconfigured (too small) budget limit; retries of expensive tasks that each consume large allocations.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Sub-agent token budget scope must not be empty
- Agent continuation target is no longer retained
- Agent continuation target is outside the active session
- Agent not found
- Agent session not found
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/7e28156275afdcad.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:5255
});
let Some((scope_id, limit)) = scope else {
return Ok(None);
};
self.resolve_budget_scope(scope_id, limit).map(Some)
}
fn resolve_budget_scope(&self, scope_id: String, limit: u64) -> Result<AgentUsageBudgetScope> {
if scope_id.trim().is_empty() {
return Err(anyhow!("Sub-agent token budget scope must not be empty"));
}
let spent = self.aggregate_budget_spent(&scope_id);
let remaining = limit.saturating_sub(spent);
let work_remaining = remaining
.saturating_sub(budget_handback::token_reserve(limit))
.saturating_sub(self.reserved_handback_tokens(&scope_id, Some(&scope_id)));
if work_remaining < MIN_SUBAGENT_SPAWN_TOKEN_RESERVE {
return Err(anyhow!(
"Sub-agent token budget exhausted for scope {scope_id}: {spent}/{limit} tokens spent, {remaining} remaining (including reserved reporting allowance). Wait for the parent/Workflow to summarize results or start a fresh agent run."
));
}
Ok(AgentUsageBudgetScope {
scope_id,
limit,
spent,
remaining,
})
}
/// Follow both delegation and continuation edges. A set makes malformed
/// persisted cycles finite and ensures shared descendants count once.
fn budget_ancestors(&self, worker_id: &str) -> BTreeSet<String> {
let mut ancestors = BTreeSet::new();
let mut pending = vec![worker_id.to_string()];
while let Some(id) = pending.pop() {
if !ancestors.insert(id.clone()) {View on GitHub (pinned to 433685b202)