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

  1. Route the control command through the shared parent agent instead of agent-to-agent
  2. Only target agent_ids that are descendants of the caller (children the caller spawned)
  3. 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

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


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)