{"record":{"id":"7e28156275afdcad","repo":"Hmbown/CodeWhale","slug":"sub-agent-token-budget-exhausted-for-scope-scope-id-spent","errorCode":null,"errorMessage":"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.","messagePattern":"Sub-agent token budget exhausted for scope (.+?): (.+?)/(.+?) tokens spent, (.+?) remaining \\(including reserved reporting allowance\\)\\. Wait for the parent/Workflow to summarize results or start a fresh agent run\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":5255,"sourceCode":"        });\n\n        let Some((scope_id, limit)) = scope else {\n            return Ok(None);\n        };\n        self.resolve_budget_scope(scope_id, limit).map(Some)\n    }\n\n    fn resolve_budget_scope(&self, scope_id: String, limit: u64) -> Result<AgentUsageBudgetScope> {\n        if scope_id.trim().is_empty() {\n            return Err(anyhow!(\"Sub-agent token budget scope must not be empty\"));\n        }\n        let spent = self.aggregate_budget_spent(&scope_id);\n        let remaining = limit.saturating_sub(spent);\n        let work_remaining = remaining\n            .saturating_sub(budget_handback::token_reserve(limit))\n            .saturating_sub(self.reserved_handback_tokens(&scope_id, Some(&scope_id)));\n        if work_remaining < MIN_SUBAGENT_SPAWN_TOKEN_RESERVE {\n            return Err(anyhow!(\n                \"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.\"\n            ));\n        }\n        Ok(AgentUsageBudgetScope {\n            scope_id,\n            limit,\n            spent,\n            remaining,\n        })\n    }\n\n    /// Follow both delegation and continuation edges. A set makes malformed\n    /// persisted cycles finite and ensures shared descendants count once.\n    fn budget_ancestors(&self, worker_id: &str) -> BTreeSet<String> {\n        let mut ancestors = BTreeSet::new();\n        let mut pending = vec![worker_id.to_string()];\n        while let Some(id) = pending.pop() {\n            if !ancestors.insert(id.clone()) {","sourceCodeStart":5237,"sourceCodeEnd":5273,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/tools/subagent/mod.rs#L5237-L5273","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"let spent = engine.aggregate_budget_spent(&scope_id);\nif limit.saturating_sub(spent) < MIN_WORK_TOKENS { /* summarize or restart before spawning */ }","typeGuard":null,"tryCatchPattern":"match engine.spawn_sub_agent(scope_id, task) {\n    Err(e) if e.to_string().contains(\"token budget exhausted\") => {\n        let summary = parent_summarize_results();\n        start_fresh_scope_run(summary);\n    }\n    other => other?,\n}","preventionTips":["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."],"tags":["rust","subagent","budget","token-limit"],"backgroundTag":"value-out-of-range","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}