Hmbown/CodeWhale · error
parent turn is already settling foreground work as
Error message
parent turn is already settling foreground work as {settlement:?}; refusing to launch child {agent_id} after the ownership barrier closed What it means
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.
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
Example fix
// before: unconditionally spawn
let child = runtime.spawn_child(agent_id)?;
// after: guard on settlement first
if let Some(settlement) = registry.current_settlement() {
return Ok(None); // parent already settling; skip child
}
let child = runtime.spawn_child(agent_id)?; Defensive patterns
Strategy: validation
Validate before calling
if let Some(settlement) = registry.current_settlement() {
return Err(anyhow!("parent settling as {settlement:?}; skip child spawn"));
} Type guard
fn can_spawn(registry: &AgentRegistry, token: &CancellationToken) -> bool {
registry.peek_settlement(token).is_none()
} Try / catch
match registry.register(self.cancel_token.clone(), agent_id) {
Ok(guard) => Ok(Some(guard)),
Err(settlement) => {
tracing::warn!(%agent_id, ?settlement, "child spawn refused: ownership barrier closed");
Ok(None)
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Agent not found
- cancel wins race
- Cannot queue a parent message for agent
- lane ` ` was stopped before tmux dry-run start completed
- lane ` ` was stopped before tmux launch
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/84f98692e74293c1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:3038
runtime.foreground_children = None;
runtime
}
fn foreground_child_registration(
&self,
agent_id: &str,
) -> Result<Option<ForegroundChildRegistration>> {
if self.spawn_depth == 0 {
return Ok(None);
}
let Some(registry) = self.foreground_children.as_ref() else {
return Ok(None);
};
registry
.register(self.cancel_token.clone(), agent_id)
.map(Some)
.map_err(|settlement| {
anyhow!(
"parent turn is already settling foreground work as {settlement:?}; refusing to launch child {agent_id} after the ownership barrier closed"
)
})
}
/// Build a child runtime cloning this one, incrementing `spawn_depth`,
/// and deriving a child cancellation token. Used at spawn entry to
/// construct the runtime the new sub-agent will see.
///
/// Children inherit the parent's approval state. A non-auto parent can
/// still delegate read-only investigation, but approval-gated child tools
/// are blocked by the sub-agent registry instead of being silently run
/// without a prompt.
#[must_use]
pub fn child_runtime(&self) -> Self {
let mut child_context = self.context.clone();
child_context.auto_approve = self.context.auto_approve;
child_context.approval_mode = self.context.approval_mode;View on GitHub (pinned to 73e0f67d83)