{"record":{"id":"570e5c90ea302b5c","repo":"Hmbown/CodeWhale","slug":"sub-agent-admission-limit-reached-max-admitted","errorCode":null,"errorMessage":"Sub-agent admission limit reached (max_admitted {}, admitted {}, running {}, queued {}). Wait for queued/running agents to finish, cancel unneeded agents, or raise [subagents] max_admitted for this Workflow.","messagePattern":"Sub-agent admission limit reached \\(max_admitted (.+?), admitted (.+?), running (.+?), queued (.+?)\\)\\. Wait for queued/running agents to finish, cancel unneeded agents, or raise \\[subagents\\] max_admitted for this Workflow\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":6203,"sourceCode":"                    && agent.task_handle.is_some()\n                    && !self.running_heartbeat_timed_out(agent)\n                    && self\n                        .worker_records\n                        .get(&agent.id)\n                        .is_some_and(|record| record.status == AgentWorkerStatus::Queued)\n            })\n            .count()\n    }\n\n    /// Count admitted workers not currently in the queued launch state.\n    pub fn active_count(&self) -> usize {\n        self.admitted_count().saturating_sub(self.queued_count())\n    }\n\n    fn check_admission_capacity(&self) -> Result<()> {\n        let admitted = self.admitted_count();\n        if admitted >= self.max_admitted_agents {\n            return Err(anyhow!(\n                \"Sub-agent admission limit reached (max_admitted {}, admitted {}, running {}, queued {}). Wait for queued/running agents to finish, cancel unneeded agents, or raise [subagents] max_admitted for this Workflow.\",\n                self.max_admitted_agents,\n                admitted,\n                self.active_count(),\n                self.queued_count()\n            ));\n        }\n        Ok(())\n    }\n\n    fn running_heartbeat_timed_out(&self, agent: &SubAgent) -> bool {\n        agent.status == SubAgentStatus::Running\n            && agent.task_handle.is_some()\n            && agent.last_activity_at.elapsed() >= self.running_heartbeat_timeout\n    }\n\n    pub fn touch(&mut self, agent_id: &str) -> bool {\n        let Some(agent) = self.agents.get_mut(agent_id) else {","sourceCodeStart":6185,"sourceCodeEnd":6221,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/subagent/mod.rs#L6185-L6221","documentation":"The manager enforces an admission ceiling: when admitted_count() (queued plus running children) reaches max_admitted_agents, new spawns are refused with this backpressure error, which reports the cap and the current admitted/running/queued split. This is designed flow control, not corruption — the message itself lists the remedies (wait, cancel, or raise [subagents] max_admitted). The cap can be raised at runtime via with_admission_limit, clamped to a global maximum.","triggerScenarios":"Spawning more sub-agents than max_admitted before earlier ones finish; fan-out (map over many tasks) exceeding the configured ceiling; stuck Running agents (e.g., heartbeat issues or very long tasks) holding all admitted slots; queued agents counting toward admitted while launch concurrency is lower.","commonSituations":"Large parallel workflows (batch refactors, multi-file research) exceeding the default admission cap; one slow worker blocking a whole wave of new spawns; admission reduced for testing and forgotten before a production run.","solutions":["Wait for completion events from queued/running agents and retry the spawn when a slot frees","Cancel unneeded agents to release admitted slots immediately","Raise the limit: set [subagents] max_admitted in config (or with_admission_limit at construction) sized for the workflow's real fan-out","Audit for stuck Running agents via manager.list() — reaping them frees slots without new work"],"exampleFix":"// before\nlet agent = spawn(request).await?;\n\n// after\nloop {\n    match spawn(request.clone()).await {\n        Ok(agent) => break Ok(agent),\n        Err(e) if e.to_string().contains(\"admission limit reached\") => {\n            wait_for_agent_completion_event().await; // backpressure, then retry\n        }\n        Err(e) => break Err(e),\n    }\n}","handlingStrategy":"retry","validationCode":"// Cheap gate before spawning in fan-out loops.\nlet admitted = manager.list().len(); // queued + running snapshot\nif admitted >= expected_admission_cap {\n    wait_for_completion_event().await; // backpressure before the call\n}","typeGuard":null,"tryCatchPattern":"Loop the spawn on errors whose message contains 'admission limit reached': await one agent completion event per retry so the loop always makes progress; cap total wait time and surface the last error on timeout.","preventionTips":["Size [subagents] max_admitted (or with_admission_limit) to the workflow's genuine parallelism before running fan-outs","Consume completion events to drive the next spawn instead of spawning the whole batch up front","Cancel agents whose output is no longer needed — cancelled slots free admission immediately"],"tags":["subagent","rust","admission-control","capacity","backpressure","config"],"backgroundTag":"capacity-limit-exceeded","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}