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

  1. Do not spawn sub-agents from final/cleanup messages — issue the tool call while the turn is still active
  2. Handle the error by reporting the child as not launched and letting the parent's settlement finish
  3. Check turn/cancel state (registry.peek/settlement) before requesting a spawn
  4. 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

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


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)