Hmbown/CodeWhale · error
Agent continuation target is no longer retained
Error message
Agent continuation target is no longer retained
What it means
Thrown by `continuation_target` when the walk follows a `resume_targets` pointer to a successor id that no longer exists in `self.agents`. The lineage points at a record that was reaped or dropped, so the fork target cannot be established and the operation fails closed rather than silently truncating the chain.
Solutions
- Re-check the registry roster and drop stale resume_targets entries pointing at removed agents.
- Continue from the last live agent in the chain instead of the dead successor (prune the lineage).
- Avoid reusing agent records across registry/session resets; re-spawn the child.
- If reaping is concurrent, hold the registry lock for the whole resolve+continue operation.
Example fix
// before
let target = registry.continuation_target(&agent_id)?;
// after
let target = match registry.continuation_target(&agent_id) {
Ok(t) => t,
Err(e) if e.to_string().contains("no longer retained") => {
registry.prune_dead_resume_targets(&agent_id);
agent_id.to_string() // continue from the last live record
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: fallback
Validate before calling
let mut cur = agent_id.to_string();
while let Some(next) = registry.resume_target_of(&cur) {
if !registry.contains_agent(&next) {
anyhow::bail!("lineage successor {next} is not retained");
}
cur = next;
} Try / catch
let target = registry.continuation_target(agent_id).or_else(|_| {
registry.prune_dead_resume_targets(agent_id);
Ok(agent_id.to_string()) // continue from the last live record
})?; Prevention
- Reap finished children only after clearing inbound resume_targets references.
- Hold the registry lock across resolve+continue to avoid a concurrent-removal race.
- Rebuild the roster after session switches before resuming any lineage.
- Prune dead resume_targets on registry load.
When it happens
Trigger: Continuing an agent whose resume chain references a successor that was removed from the registry — e.g. the continuation child finished and was reaped, or the registry was reset while old resume_targets entries survived.
Common situations: Holding a reference to a parent across a session switch that rebuilt the registry; concurrent reaping of finished children while another thread continues the parent; stale persisted state loaded into a fresh registry with fewer records.
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 not found
- Agent continuation target is outside the active session
- Agent session not found
- Agent session name ' ' is ambiguous; use an agent_id
- agent should stay registered
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/cae7a50114938e49.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:6002
/// Follow the persisted continuation chain without treating deliberate
/// `start + resume_from` forks as continuation targets. Every hop retains
/// the original root conversation; malformed or cyclic state fails closed.
fn continuation_target(&self, agent_id: &str) -> Result<String> {
let source = self
.agents
.get(agent_id)
.ok_or_else(|| anyhow!("Agent not found"))?;
let owner = &source.owner_session_id;
let mut target = agent_id.to_string();
let mut seen = HashSet::new();
while let Some(next) = self.resume_targets.get(&target) {
if !seen.insert(target.clone()) {
return Err(anyhow!("Invalid agent continuation lineage: cycle"));
}
let successor = self
.agents
.get(next)
.ok_or_else(|| anyhow!("Agent continuation target is no longer retained"))?;
if owner.is_empty() || successor.owner_session_id != *owner {
return Err(anyhow!(
"Agent continuation target is outside the active session"
));
}
target.clone_from(next);
}
Ok(target)
}
fn continuation_source(&self, agent_id: &str) -> Option<String> {
self.resume_targets.iter().find_map(|(source, target)| {
(target == agent_id && self.continuation_target(source).is_ok()).then(|| source.clone())
})
}
pub(super) fn continuation_target_for_caller(
&self,View on GitHub (pinned to 73e0f67d83)