Hmbown/CodeWhale · error · anyhow::Error
Refusing from agent ' ' to ' '; a child may control only…
Error message
Refusing {action} from agent '{caller}' to '{agent_id}'; a child may control only its own descendants. What it means
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.
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
Example fix
// before control_agent(sibling_id, Action::Terminate); // Err: not a descendant // after control_agent(parent_id, Action::RequestTerminateChild(sibling_id)); // ask the parent
Defensive patterns
Strategy: validation
Validate before calling
fn is_descendant(caller_id: &str, target_id: &str, parents: &HashMap<String, String>) -> bool {
let mut cur = Some(target_id.to_string());
while let Some(c) = cur {
if c == caller_id { return true; }
cur = parents.get(&c).cloned();
}
false
} Type guard
fn targets_own_subtree(caller_id: &str, target_id: &str, parents: &HashMap<String, String>) -> bool {
is_descendant(caller_id, target_id, parents)
} Try / catch
match control_agent(target_id, action) {
Err(e) if e.to_string().contains("may control only its own descendants") => route_via_parent(target_id, action),
Err(e) => return Err(e),
Ok(r) => Ok(r),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.
Related errors
- read-only roles may revise their private working notes
- Refusing on self (agent_id ' '); child coordination…
- [role.posture.denied] Tool
- [shell.readonly.command] Tool
- agent action=claim widens an enforced write scope, and the…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/185ef633b6a4f1e6.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:7601
}
// The original parent edge is immutable. A resumed parent owns
// the same descendants through the explicit continuation map;
// deliberate start+resume_from forks grant no such authority.
let parent_identity = if self.agents.contains_key(parent_ref) {
self.continuation_target(parent_ref)?
} else {
parent_ref.to_string()
};
if parent_identity == caller_identity {
return Ok(agent_id);
}
let Some((parent_id, _)) = self.worker_record_by_ref(parent_ref) else {
break;
};
cursor = parent_id;
}
Err(anyhow!(
"Refusing {action} from agent '{caller}' to '{agent_id}'; a child may control only its own descendants."
))
}
pub(super) fn ensure_caller_controls_descendant_for_session(
&self,
active_session_id: &str,
agent_ref: &str,
caller_agent_id: Option<&str>,
action: &str,
) -> Result<String> {
let agent_id = self.resolve_agent_ref_for_session(active_session_id, agent_ref)?;
if let Some(caller) = caller_agent_id
.map(str::trim)
.filter(|caller| !caller.is_empty() && *caller != "root")
{
self.resolve_agent_ref_for_session(active_session_id, caller)?;
}View on GitHub (pinned to 73e0f67d83)