{"record":{"id":"84f98692e74293c1","repo":"Hmbown/CodeWhale","slug":"parent-turn-is-already-settling-foreground-work-as","errorCode":null,"errorMessage":"parent turn is already settling foreground work as {settlement:?}; refusing to launch child {agent_id} after the ownership barrier closed","messagePattern":"parent turn is already settling foreground work as (.+?); refusing to launch child (.+?) after the ownership barrier closed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":3038,"sourceCode":"        runtime.foreground_children = None;\n        runtime\n    }\n\n    fn foreground_child_registration(\n        &self,\n        agent_id: &str,\n    ) -> Result<Option<ForegroundChildRegistration>> {\n        if self.spawn_depth == 0 {\n            return Ok(None);\n        }\n        let Some(registry) = self.foreground_children.as_ref() else {\n            return Ok(None);\n        };\n        registry\n            .register(self.cancel_token.clone(), agent_id)\n            .map(Some)\n            .map_err(|settlement| {\n                anyhow!(\n                    \"parent turn is already settling foreground work as {settlement:?}; refusing to launch child {agent_id} after the ownership barrier closed\"\n                )\n            })\n    }\n\n    /// Build a child runtime cloning this one, incrementing `spawn_depth`,\n    /// and deriving a child cancellation token. Used at spawn entry to\n    /// construct the runtime the new sub-agent will see.\n    ///\n    /// Children inherit the parent's approval state. A non-auto parent can\n    /// still delegate read-only investigation, but approval-gated child tools\n    /// are blocked by the sub-agent registry instead of being silently run\n    /// without a prompt.\n    #[must_use]\n    pub fn child_runtime(&self) -> Self {\n        let mut child_context = self.context.clone();\n        child_context.auto_approve = self.context.auto_approve;\n        child_context.approval_mode = self.context.approval_mode;","sourceCodeStart":3020,"sourceCodeEnd":3056,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/subagent/mod.rs#L3020-L3056","documentation":"When launching a sub-agent, the child must register with the parent's cancellation/ownership registry. If the parent turn has already entered settlement (e.g. it is completing, failing, or cancelling foreground work), registration returns that settlement state and the tool refuses to spawn the child — no new agent may start after the ownership barrier closes. This prevents orphaned sub-agents outliving the turn that owns them.","triggerScenarios":"Calling the sub-agent spawn path after the parent turn began settling (completion/failure/cancel initiated): `registry.register(cancel_token, agent_id)` returns the existing settlement variant, which is mapped to this error.","commonSituations":"A model requests a sub-agent in its final message while the turn is wrapping up; a race where cancellation/timeout fires between the model's tool-call decision and the spawn; nested agents spawned from cleanup paths.","solutions":["Do not spawn sub-agents from final/cleanup messages — issue the tool call while the turn is still active","Handle the error by reporting the child as not launched and letting the parent's settlement finish","Check turn/cancel state (registry.peek/settlement) before requesting a spawn","If this fires spuriously, investigate the race between cancel_token firing and the spawn call ordering"],"exampleFix":"// before: unconditionally spawn\nlet child = runtime.spawn_child(agent_id)?;\n// after: guard on settlement first\nif let Some(settlement) = registry.current_settlement() {\n    return Ok(None); // parent already settling; skip child\n}\nlet child = runtime.spawn_child(agent_id)?;","handlingStrategy":"validation","validationCode":"if let Some(settlement) = registry.current_settlement() {\n    return Err(anyhow!(\"parent settling as {settlement:?}; skip child spawn\"));\n}","typeGuard":"fn can_spawn(registry: &AgentRegistry, token: &CancellationToken) -> bool {\n    registry.peek_settlement(token).is_none()\n}","tryCatchPattern":"match registry.register(self.cancel_token.clone(), agent_id) {\n    Ok(guard) => Ok(Some(guard)),\n    Err(settlement) => {\n        tracing::warn!(%agent_id, ?settlement, \"child spawn refused: ownership barrier closed\");\n        Ok(None)\n    }\n}","preventionTips":["Spawn sub-agents only while the turn is active, never from final messages","Check settlement state before requesting a spawn","Treat refusal as expected cancellation, not a bug","Avoid long delays between the model's tool call and spawn execution"],"tags":["subagent","lifecycle","race-condition"],"backgroundTag":"invalid-state-transition","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}