Hmbown/CodeWhale · error
Refusing to interrupt root. agents/interrupt fails closed on
Error message
Refusing to interrupt root. agents/interrupt fails closed on the root session.
What it means
interrupt_child hard-refuses the literal reference "root" (case-insensitive, whitespace-trimmed): the agents/interrupt tool fails closed on the root session because interrupting root would tear down the orchestrating session itself, which the sub-agent API must never do. Only actual children are interruptible; stopping the root is the host UI's job (e.g., the user's interrupt key).
Source
Thrown at crates/tui/src/tools/subagent/mod.rs:5793
resume_prompt,
assignment,
allowed_tools,
options,
)?;
self.resume_targets
.insert(agent_id, resumed.agent_id.clone());
Ok(resumed)
}
/// Interrupt a child, preserve checkpoint, fail closed on root/self.
pub fn interrupt_child(
&mut self,
agent_ref: &str,
caller_agent_id: Option<&str>,
reason: String,
) -> Result<(SubAgentResult, SubAgentResult)> {
if agent_ref.trim().eq_ignore_ascii_case("root") {
return Err(anyhow!(
"Refusing to interrupt root. agents/interrupt fails closed on the root session."
));
}
let agent_id = self.resolve_agent_ref(agent_ref)?;
self.ensure_caller_controls_descendant(&agent_id, caller_agent_id, "agents/interrupt")?;
let prior = self.get_result_by_ref(&agent_id)?;
if prior.status != SubAgentStatus::Running
|| self
.agents
.get(&agent_id)
.is_some_and(|agent| agent.completion_claimed)
{
return Ok((prior.clone(), prior));
}
// Build a continuable checkpoint from the latest stored checkpoint or a
// minimal placeholder so interrupt never drops recoverability silently.View on GitHub (pinned to 0c42157ee5)
Solutions
- Target a real child: pass an agent_id or session name obtained from the spawn receipt or manager.list()
- To stop the whole run, use the host application's own root interrupt (UI interrupt action), not the agents tool
- Validate refs before the call and reject the root alias in your own orchestration layer with a clearer message
Example fix
// before
manager.interrupt_child("root", None, reason)?;
// after
let targets: Vec<String> = manager.list().into_iter()
.filter(|s| matches!(s.status, SubAgentStatus::Running))
.map(|s| s.agent_id)
.collect();
for id in targets {
manager.interrupt_child(&id, None, reason.clone())?;
} Defensive patterns
Strategy: validation
Validate before calling
// Reject the root alias before it reaches the manager.
anyhow::ensure!(
!agent_ref.trim().eq_ignore_ascii_case("root"),
"agents/interrupt cannot target root; pass a child agent_id or session name"
); Try / catch
On 'Refusing to interrupt root', map the action to the host's own root interrupt (or interrupt each running child by id); the error is a permanent refusal, never retried as-is.
Prevention
- Validate tool arguments against the documented ref grammar (child id or session name) in your own layer
- When the intent is 'stop everything', enumerate running children via list() and interrupt each
- Prompt-engineer agents to interrupt by specific agent_id, never by the root alias
When it happens
Trigger: Calling interrupt_child("root", caller, reason) — the guard fires before any resolution; a model confusing the root session with a worker and emitting agent_ref="root"; template code that passes a fixed "root" target.
Common situations: LLM tool-calls with agent_ref="root" because the prompt mentions the root session; user commands like 'interrupt everything' being translated to the root ref by glue code; copy-pasted examples using "root" as a placeholder.
Related errors
- fleet authority mismatch at the spawn boundary: the receipt
- external {} credential file {} exceeds the {} byte safety li
- Cannot follow up agent {agent_id}: status is {} and the chil
- Cannot continue agent {agent_id}: no runtime is available to
- Cannot continue agent {agent_id}: status is {} and the child
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/0e92895035521aa7.
Report an issue: GitHub.