Hmbown/CodeWhale · error · anyhow::Error
Refusing on self (agent_id ' '); child coordination…
Error message
Refusing {action} on self (agent_id '{agent_id}'); child coordination authority is limited to strict descendants. What it means
Subagent coordination authority is limited to strict descendants: an agent is never allowed to control itself. When the resolved caller identity equals the target agent's continuation identity, the operation (e.g. interrupt, terminate, send-a-command) is refused with this error.
Solutions
- Address the command at a child agent_id instead of the calling agent itself
- Skip the control call when target == caller in your tool-invocation logic
- Check the target id against the caller's own id before dispatching
Example fix
// before
control_agent(&caller_id, Action::Interrupt); // Err: refusing action on self
// after
if target_id != caller_id { control_agent(&target_id, Action::Interrupt); } Defensive patterns
Strategy: validation
Validate before calling
fn ensure_not_self(caller_id: &str, target_id: &str, action: &str) -> Result<(), String> {
if caller_id == target_id { Err(format!("refusing {action} on self")) } else { Ok(()) }
} Type guard
fn is_self_action(caller_id: &str, target_id: &str) -> bool {
caller_id == target_id
} Try / catch
match control_agent(target_id, action) {
Err(e) if e.to_string().contains("Refusing") && e.to_string().contains("on self") => Ok(()), // no-op: acting on self is expected to be refused
Err(e) => return Err(e),
Ok(r) => Ok(r),
} Prevention
- Compare target agent_id against the caller's own id before dispatching any control command
- Have the LLM prompt/tool schema reject self-addressed targets early
- Log agent_id at spawn so prompts always reference the correct child ids
When it happens
Trigger: A tool invocation where the caller and target agent_id resolve to the same continuation target — typically an agent issuing a control command addressed to itself, directly or via a self-referencing name.
Common situations: An LLM subagent mistakenly addressing its own name/id in an agent control tool call; loops where an agent retries an operation against itself after a partial failure.
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 from agent ' ' to ' '; a child may control only…
- [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/4e01747e9ed7751e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:7563
) -> Result<String> {
let agent_id = self.resolve_agent_ref(agent_ref)?;
let Some(caller) = caller_agent_id
.map(str::trim)
.filter(|caller| !caller.is_empty() && *caller != "root")
else {
return Ok(agent_id);
};
let caller_identity = if self.agents.contains_key(caller) {
self.continuation_target(caller)?
} else {
// Internal headless parents can own a worker record without a
// paired SubAgent projection. A missing caller still fails closed.
self.worker_record_by_ref(caller)
.map(|(id, _)| id)
.ok_or_else(|| anyhow!("Calling agent not found"))?
};
if caller_identity == self.continuation_target(&agent_id)? {
return Err(anyhow!(
"Refusing {action} on self (agent_id '{agent_id}'); child coordination authority is limited to strict descendants."
));
}
let mut cursor = agent_id.clone();
let mut visited = std::collections::HashSet::new();
while visited.insert(cursor.clone()) {
let Some((_, record)) = self.worker_record_by_ref(&cursor) else {
break;
};
let Some(parent_ref) = record
.parent_run_id
.as_deref()
.or(record.spec.parent_run_id.as_deref())
else {
break;
};
if parent_ref == "root" {View on GitHub (pinned to 73e0f67d83)