{"record":{"id":"0e92895035521aa7","repo":"Hmbown/CodeWhale","slug":"refusing-to-interrupt-root-agents-interrupt-fails","errorCode":null,"errorMessage":"Refusing to interrupt root. agents/interrupt fails closed on the root session.","messagePattern":"Refusing to interrupt root\\. agents/interrupt fails closed on the root session\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":5793,"sourceCode":"            resume_prompt,\n            assignment,\n            allowed_tools,\n            options,\n        )?;\n        self.resume_targets\n            .insert(agent_id, resumed.agent_id.clone());\n        Ok(resumed)\n    }\n\n    /// Interrupt a child, preserve checkpoint, fail closed on root/self.\n    pub fn interrupt_child(\n        &mut self,\n        agent_ref: &str,\n        caller_agent_id: Option<&str>,\n        reason: String,\n    ) -> Result<(SubAgentResult, SubAgentResult)> {\n        if agent_ref.trim().eq_ignore_ascii_case(\"root\") {\n            return Err(anyhow!(\n                \"Refusing to interrupt root. agents/interrupt fails closed on the root session.\"\n            ));\n        }\n        let agent_id = self.resolve_agent_ref(agent_ref)?;\n        self.ensure_caller_controls_descendant(&agent_id, caller_agent_id, \"agents/interrupt\")?;\n\n        let prior = self.get_result_by_ref(&agent_id)?;\n        if prior.status != SubAgentStatus::Running\n            || self\n                .agents\n                .get(&agent_id)\n                .is_some_and(|agent| agent.completion_claimed)\n        {\n            return Ok((prior.clone(), prior));\n        }\n\n        // Build a continuable checkpoint from the latest stored checkpoint or a\n        // minimal placeholder so interrupt never drops recoverability silently.","sourceCodeStart":5775,"sourceCodeEnd":5811,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/subagent/mod.rs#L5775-L5811","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nmanager.interrupt_child(\"root\", None, reason)?;\n\n// after\nlet targets: Vec<String> = manager.list().into_iter()\n    .filter(|s| matches!(s.status, SubAgentStatus::Running))\n    .map(|s| s.agent_id)\n    .collect();\nfor id in targets {\n    manager.interrupt_child(&id, None, reason.clone())?;\n}","handlingStrategy":"validation","validationCode":"// Reject the root alias before it reaches the manager.\nanyhow::ensure!(\n    !agent_ref.trim().eq_ignore_ascii_case(\"root\"),\n    \"agents/interrupt cannot target root; pass a child agent_id or session name\"\n);","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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"],"tags":["subagent","rust","root-session","fail-closed","guard","interrupt"],"backgroundTag":"operation-not-permitted","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}