{"record":{"id":"4e01747e9ed7751e","repo":"Hmbown/CodeWhale","slug":"refusing-action-on-self-agent-id-agent-id","errorCode":null,"errorMessage":"Refusing {action} on self (agent_id '{agent_id}'); child coordination authority is limited to strict descendants.","messagePattern":"Refusing (.+?) on self \\(agent_id '(.+?)'\\); child coordination authority is limited to strict descendants\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":6783,"sourceCode":"    /// descendant of the calling agent. Root registries carry no caller id\n    /// (or the literal `root`) and retain authority over every child. This\n    /// prevents a child from messaging, waking, or interrupting a sibling or\n    /// ancestor and then claiming the ownership that target released.\n    pub(super) fn ensure_caller_controls_descendant(\n        &self,\n        agent_ref: &str,\n        caller_agent_id: Option<&str>,\n        action: &str,\n    ) -> Result<String> {\n        let agent_id = self.resolve_agent_ref(agent_ref)?;\n        let Some(caller) = caller_agent_id\n            .map(str::trim)\n            .filter(|caller| !caller.is_empty() && *caller != \"root\")\n        else {\n            return Ok(agent_id);\n        };\n        if caller == agent_id {\n            return Err(anyhow!(\n                \"Refusing {action} on self (agent_id '{agent_id}'); child coordination authority is limited to strict descendants.\"\n            ));\n        }\n\n        let mut cursor = agent_id.clone();\n        let mut visited = std::collections::HashSet::new();\n        while visited.insert(cursor.clone()) {\n            let Some((_, record)) = self.worker_record_by_ref(&cursor) else {\n                break;\n            };\n            let Some(parent_ref) = record\n                .parent_run_id\n                .as_deref()\n                .or(record.spec.parent_run_id.as_deref())\n            else {\n                break;\n            };\n            if parent_ref == caller {","sourceCodeStart":6765,"sourceCodeEnd":6801,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/subagent/mod.rs#L6765-L6801","documentation":"Thrown by the sub-agent manager's coordination guard `ensure_caller_controls_descendant` (crates/tui/src/tools/subagent/mod.rs:6769) when a child agent attempts a control action (stop, pause, status, etc.) on itself. A child's coordination authority is deliberately limited to strict descendants; the guard first resolves the target ref, then refuses when the resolved agent_id equals the non-root caller id. Root callers (None, empty, or \"root\") bypass this check entirely, so it only fires for child-agent callers.","triggerScenarios":"A sub-agent whose caller_agent_id is a non-empty, non-\"root\" id calls a control API with an agent_ref that `resolve_agent_ref` maps to the caller's own agent_id — e.g. passing \"self\", \"me\", an alias, or the literal id equal to the caller.","commonSituations":"Orchestrator prompts like \"manage all your workers\" where the child enumerates agents including itself; agent_ref aliases that resolve back to the caller; root-session commands copied into a child prompt and replayed verbatim.","solutions":["Filter the caller's own agent_id out of any enumerated agent list before issuing control actions.","If the action genuinely must apply to that agent, issue it from the root session (caller None/\"root\"), where the guard passes.","Target only refs of agents this child spawned, or spawn the target under this child first so it is a strict descendant.","For self-termination, use the child's normal completion path (final report / finish) instead of a control action."],"exampleFix":"// before (inside a child agent, caller_agent_id = Some(child_id))\nmanager.ensure_caller_controls_descendant(\"self\", caller_agent_id, \"stop\")?; // Err 1200\n\n// after\nlet target = manager.resolve_agent_ref(agent_ref)?;\nif caller_agent_id == Some(target.as_str()) { return Ok(()); } // skip self\nmanager.ensure_caller_controls_descendant(&target, caller_agent_id, \"stop\")?;","handlingStrategy":"validation","validationCode":"fn is_self_target(resolved: &str, caller: Option<&str>) -> bool {\n    caller\n        .map(str::trim)\n        .is_some_and(|c| !c.is_empty() && c != \"root\" && c == resolved)\n}\n// before calling: if is_self_target(&manager.resolve_agent_ref(ref)?, caller) { skip }","typeGuard":"fn safe_control_target(resolved: &str, caller: Option<&str>) -> bool {\n    match caller.map(str::trim) {\n        Some(c) if !c.is_empty() && c != \"root\" => c != resolved, // not self\n        _ => true, // root caller: guard bypassed\n    }\n}","tryCatchPattern":"match manager.ensure_caller_controls_descendant(&agent_ref, caller, action) {\n    Err(e) if e.to_string().contains(\"on self\") => { /* skip self-targeted action, not fatal */ }\n    other => other?,\n}","preventionTips":["Never pass self-referencing aliases (\"self\", \"me\") from child prompts.","Filter the caller's own id out of any enumerated agent list before acting.","Route cross-branch or fleet-wide control through the root session."],"tags":["subagent","authorization","self-reference","rust","codewhale"],"backgroundTag":"agent-hierarchy-permission-denied","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}