Hmbown/CodeWhale · error · anyhow::Error
Calling agent not found
Error message
Calling agent not found
What it means
Authority checks for subagent coordination require the calling agent's identity. Internal headless parents can own a worker record without a paired SubAgent projection, and when even the worker record lookup fails the check fails closed with this error because the caller's identity cannot be established.
Solutions
- Re-open or re-derive the subagent session so caller records are rebuilt from the authoritative state
- Check the subagent state root for a deleted/corrupt worker record and restore or re-spawn the parent
- Retry after the current operation finishes — the caller may have been a transient headless parent
Example fix
// before
let caller_id = self.worker_record_by_ref(caller).map(|(id,_)| id).ok_or_else(|| anyhow!("Calling agent not found"))?;
// after (caller side)
if !self.has_agent(caller) { return Ok(Status::caller_gone); } // handle missing caller before invoking the control tool Defensive patterns
Strategy: try-catch
Validate before calling
fn caller_is_known(state: &SubAgentState, caller: &str) -> bool {
state.worker_records.iter().any(|r| r.ref_name == caller) || state.agents.iter().any(|a| a.id == caller)
} Type guard
fn has_worker_record(state: &SubAgentState, caller: &str) -> bool {
state.worker_records.iter().any(|r| r.ref_name == caller)
} Try / catch
match ensure_caller_authority(state, caller, target) {
Err(e) if e.to_string() == "Calling agent not found" => {
// caller record vanished; rebuild state from disk or abort the coordination op gracefully
rebuild_state_and_retry_or_abort()
}
Err(e) => return Err(e),
Ok(()) => proceed(),
} Prevention
- Don't delete or hand-edit files under the subagent state root while a session is live
- Verify the caller is still active before issuing coordination commands
- Re-derive caller refs from the current session after any restart
When it happens
Trigger: A coordination tool call (control/send/interrupt) where `caller` resolves neither to a continuation target SubAgent nor to a worker record — e.g. an unknown or already-removed caller ref passed by the engine or a stale record after cleanup.
Common situations: A caller agent being reaped mid-operation; a corrupted or manually edited subagent state directory dropping the worker record; engine passing an obsolete caller ref after session restart.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Agent continuation target is no longer retained
- Agent continuation target is outside the active session
- Agent not found
- Agent session not found
- Agent session name ' ' is ambiguous; use an agent_id
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/dfd9e54d9784bf15.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:7560
agent_ref: &str,
caller_agent_id: Option<&str>,
action: &str,
) -> 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 {View on GitHub (pinned to 73e0f67d83)