Hmbown/CodeWhale · error
Invalid agent continuation lineage: cycle
Error message
Invalid agent continuation lineage: cycle
What it means
Thrown by `continuation_target` while walking the `resume_targets` chain: if the walk revisits an id already seen (HashSet insert returns false), the lineage contains a cycle and fails closed. A cyclic lineage means resume bookkeeping was corrupted, so no continuation target can be trusted.
Solutions
- Inspect and clear the offending `resume_targets` entries so the chain terminates at the real continuation root.
- Delete the corrupted persisted subagent state and let the registry rebuild lineage from checkpoints.
- Reproduce with a dump of the resume_targets chain to find the cycle edge and fix the code that wrote it.
- As a workaround, start a fresh child agent instead of continuing the one with the corrupt lineage.
Defensive patterns
Strategy: fallback
Validate before calling
let mut seen = std::collections::HashSet::new();
let mut cur = agent_id.to_string();
while let Some(next) = registry.resume_target_of(&cur) {
if !seen.insert(cur.clone()) {
anyhow::bail!("corrupt resume lineage detected before continue");
}
cur = next;
} Try / catch
match registry.continuation_target(agent_id) {
Ok(t) => t,
Err(e) if e.to_string().contains("cycle") => {
registry.clear_resume_targets(agent_id); // fall back to forking from the root
agent_id.to_string()
}
Err(e) => return Err(e),
} Prevention
- Never write resume_targets bidirectionally when forking continuations.
- Validate persisted resume lineage on load (detect cycles at restore time).
- Avoid hand-editing or partially writing subagent checkpoint state.
- On format changes, migrate or discard old resume_targets rather than loading them raw.
When it happens
Trigger: Calling continue/follow-up on an agent whose `resume_targets` map forms a loop — e.g. record A points to B and B (directly or transitively) points back to A — so the `while let Some(next) = self.resume_targets.get(&target)` walk never reaches an agent without a successor.
Common situations: Corrupted persisted resume state loaded from disk; a bug that sets resume_targets bidirectionally when forking continuations; manual editing or partial writes of checkpoint state; version changes to the resume-target format leaving stale entries.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 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/19ee0ba6fdeaab8e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:5997
) -> Result<ParentMailReceipt> {
let agent_id = self.resolve_agent_ref_for_session(active_session_id, agent_ref)?;
self.followup_child(&agent_id, text)
}
/// 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())View on GitHub (pinned to 73e0f67d83)