{"record":{"id":"cae7a50114938e49","repo":"Hmbown/CodeWhale","slug":"agent-continuation-target-is-no-longer-retained","errorCode":null,"errorMessage":"Agent continuation target is no longer retained","messagePattern":"Agent continuation target is no longer retained","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":6002,"sourceCode":"    /// Follow the persisted continuation chain without treating deliberate\n    /// `start + resume_from` forks as continuation targets. Every hop retains\n    /// the original root conversation; malformed or cyclic state fails closed.\n    fn continuation_target(&self, agent_id: &str) -> Result<String> {\n        let source = self\n            .agents\n            .get(agent_id)\n            .ok_or_else(|| anyhow!(\"Agent not found\"))?;\n        let owner = &source.owner_session_id;\n        let mut target = agent_id.to_string();\n        let mut seen = HashSet::new();\n        while let Some(next) = self.resume_targets.get(&target) {\n            if !seen.insert(target.clone()) {\n                return Err(anyhow!(\"Invalid agent continuation lineage: cycle\"));\n            }\n            let successor = self\n                .agents\n                .get(next)\n                .ok_or_else(|| anyhow!(\"Agent continuation target is no longer retained\"))?;\n            if owner.is_empty() || successor.owner_session_id != *owner {\n                return Err(anyhow!(\n                    \"Agent continuation target is outside the active session\"\n                ));\n            }\n            target.clone_from(next);\n        }\n        Ok(target)\n    }\n\n    fn continuation_source(&self, agent_id: &str) -> Option<String> {\n        self.resume_targets.iter().find_map(|(source, target)| {\n            (target == agent_id && self.continuation_target(source).is_ok()).then(|| source.clone())\n        })\n    }\n\n    pub(super) fn continuation_target_for_caller(\n        &self,","sourceCodeStart":5984,"sourceCodeEnd":6020,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/subagent/mod.rs#L5984-L6020","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet target = registry.continuation_target(&agent_id)?;\n// after\nlet target = match registry.continuation_target(&agent_id) {\n    Ok(t) => t,\n    Err(e) if e.to_string().contains(\"no longer retained\") => {\n        registry.prune_dead_resume_targets(&agent_id);\n        agent_id.to_string() // continue from the last live record\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"fallback","validationCode":"let mut cur = agent_id.to_string();\nwhile let Some(next) = registry.resume_target_of(&cur) {\n    if !registry.contains_agent(&next) {\n        anyhow::bail!(\"lineage successor {next} is not retained\");\n    }\n    cur = next;\n}","typeGuard":null,"tryCatchPattern":"let target = registry.continuation_target(agent_id).or_else(|_| {\n    registry.prune_dead_resume_targets(agent_id);\n    Ok(agent_id.to_string()) // continue from the last live record\n})?;","preventionTips":["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."],"tags":["subagent","resource-not-found","stale-reference","rust"],"backgroundTag":"entity-not-found","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}