Hmbown/CodeWhale · error · anyhow::Error
Sub-agent token budget scope must not be empty
Error message
Sub-agent token budget scope must not be empty
What it means
Thrown by `resolve_budget_scope` when a caller requests a sub-agent token-budget scope whose scope id is empty or whitespace-only. A scope id is the key under which spent tokens are aggregated and handback reserves are tracked, so an empty id cannot address any budget bucket.
Solutions
- Provide a non-empty scope id when requesting/setting the sub-agent token budget.
- Trim or reject empty scope ids at the caller's input boundary before invoking the budget API.
- Fall back to a well-known default scope name (e.g. the session or workflow id) when no explicit scope was supplied.
Example fix
// before
engine.set_token_budget(String::new(), 50_000);
// after
let scope = if scope_id.trim().is_empty() { session_id.clone() } else { scope_id };
engine.set_token_budget(scope, 50_000); Defensive patterns
Strategy: validation
Validate before calling
fn valid_scope(scope_id: &str) -> bool { !scope_id.trim().is_empty() }
assert!(valid_scope(&scope_id), "budget scope id must be non-empty"); Type guard
fn as_scope_id(raw: &str) -> Option<&str> {
let t = raw.trim();
(!t.is_empty()).then_some(t)
} Prevention
- Default to the session or workflow id when no explicit scope was configured.
- Validate/trim scope ids at every input boundary (config parsing, tool arguments).
- Never construct scope ids by string concatenation of possibly-empty fields without a fallback.
When it happens
Trigger: Calling the budget-resolution path (`resolve_budget_scope` via the entry that maps it at mod.rs:5247) with `scope_id` set to `""` or only whitespace, typically because the caller derived the scope id from an unset field, an empty workflow name, or an untrimmed user input.
Common situations: A workflow/tool forgot to set the budget scope id; a config value was parsed as empty; programmatic callers passing `String::new()` as a default scope.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Sub-agent token budget exhausted for scope
- A pinned task provider requires an explicit model
- Agent continuation target is no longer retained
- Agent continuation target is outside the active session
- Agent not found
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/b23d0706b0e6c5b6.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:5247
// cap is stored on the worker profile and checked separately; creating
// a new pool here would make its spend disappear from the parent.
let scope = self.inherited_budget_scope(parent_run_id).or_else(|| {
narrow_optional_limit(
self.default_token_budget,
positive_token_budget(requested_budget),
)
.map(|limit| (worker_id.to_string(), limit))
});
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,
})
}View on GitHub (pinned to 433685b202)