{"record":{"id":"185ef633b6a4f1e6","repo":"Hmbown/CodeWhale","slug":"refusing-action-from-agent-caller-to-agent","errorCode":null,"errorMessage":"Refusing {action} from agent '{caller}' to '{agent_id}'; a child may control only its own descendants.","messagePattern":"Refusing (.+?) from agent '(.+?)' to '(.+?)'; a child may control only its own descendants\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":7601,"sourceCode":"            }\n            // The original parent edge is immutable. A resumed parent owns\n            // the same descendants through the explicit continuation map;\n            // deliberate start+resume_from forks grant no such authority.\n            let parent_identity = if self.agents.contains_key(parent_ref) {\n                self.continuation_target(parent_ref)?\n            } else {\n                parent_ref.to_string()\n            };\n            if parent_identity == caller_identity {\n                return Ok(agent_id);\n            }\n            let Some((parent_id, _)) = self.worker_record_by_ref(parent_ref) else {\n                break;\n            };\n            cursor = parent_id;\n        }\n\n        Err(anyhow!(\n            \"Refusing {action} from agent '{caller}' to '{agent_id}'; a child may control only its own descendants.\"\n        ))\n    }\n\n    pub(super) fn ensure_caller_controls_descendant_for_session(\n        &self,\n        active_session_id: &str,\n        agent_ref: &str,\n        caller_agent_id: Option<&str>,\n        action: &str,\n    ) -> Result<String> {\n        let agent_id = self.resolve_agent_ref_for_session(active_session_id, agent_ref)?;\n        if let Some(caller) = caller_agent_id\n            .map(str::trim)\n            .filter(|caller| !caller.is_empty() && *caller != \"root\")\n        {\n            self.resolve_agent_ref_for_session(active_session_id, caller)?;\n        }","sourceCodeStart":7583,"sourceCodeEnd":7619,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/subagent/mod.rs#L7583-L7619","documentation":"The ancestor-walk for caller authority terminates at the session root without finding the target among the caller's ancestors, so the target is not a descendant of the caller. Coordination is refused because a child agent may control only its own descendants.","triggerScenarios":"Agent A attempts to interrupt/terminate/send a control command to agent B where B is A's sibling, parent, or an unrelated agent — the parent-chain walk from B never reaches A.","commonSituations":"Two parallel worker subagents trying to coordinate each other; a subagent trying to control its parent; agents spawned in different subtrees attempting cross-control after a role mix-up in prompts.","solutions":["Route the control command through the shared parent agent instead of agent-to-agent","Only target agent_ids that are descendants of the caller (children the caller spawned)","Escalate to the session owner / engine for cross-tree coordination"],"exampleFix":"// before\ncontrol_agent(sibling_id, Action::Terminate); // Err: not a descendant\n// after\ncontrol_agent(parent_id, Action::RequestTerminateChild(sibling_id)); // ask the parent","handlingStrategy":"validation","validationCode":"fn is_descendant(caller_id: &str, target_id: &str, parents: &HashMap<String, String>) -> bool {\n    let mut cur = Some(target_id.to_string());\n    while let Some(c) = cur {\n        if c == caller_id { return true; }\n        cur = parents.get(&c).cloned();\n    }\n    false\n}","typeGuard":"fn targets_own_subtree(caller_id: &str, target_id: &str, parents: &HashMap<String, String>) -> bool {\n    is_descendant(caller_id, target_id, parents)\n}","tryCatchPattern":"match control_agent(target_id, action) {\n    Err(e) if e.to_string().contains(\"may control only its own descendants\") => route_via_parent(target_id, action),\n    Err(e) => return Err(e),\n    Ok(r) => Ok(r),\n}","preventionTips":["Model cross-agent coordination as requests to the shared parent, never sibling-to-sibling","Track the parent chain when spawning so callers know their own subtree","Validate the target is a spawned child before sending control commands"],"tags":["subagent","rust","permissions"],"backgroundTag":"insufficient-permissions","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"}