Hmbown/CodeWhale · error

Agent continuation target is outside the active session

Error message

Agent continuation target is outside the active session

What it means

Thrown by `continuation_target` when a successor in the resume chain belongs to a different owner session than the source agent (or the source has an empty owner). Continuations may only fork within the active session; crossing sessions would leak conversation state, so the walk refuses.

Solutions

  1. Only continue agents that belong to the currently active session; filter the roster by owner_session_id first.
  2. If cross-session continuation is genuinely needed, import the lineage into the active session with a valid owner instead of forking it in place.
  3. Fix data that was loaded with an empty owner_session_id (re-associate the record with the active session).
  4. Spawn a fresh child in the current session carrying the needed context instead of resuming a foreign lineage.

Example fix

// before
let outcome = registry.continue_agent(&foreign_agent_id, text)?;
// after
if registry.owner_session_of(&foreign_agent_id).as_deref() != Some(active_session_id) {
    anyhow::bail!("agent belongs to another session; spawn a child in this session instead");
}
let outcome = registry.continue_agent(&foreign_agent_id, text)?;
Defensive patterns

Strategy: validation

Validate before calling

let owner = registry.owner_session_of(agent_id);
if owner.as_deref() != Some(active_session_id) {
    anyhow::bail!("agent {agent_id} belongs to session {:?}, not the active session", owner);
}

Type guard

fn belongs_to_session(owner: &Option<String>, active: &str) -> bool {
    owner.as_deref() == Some(active) && !active.is_empty()
}

Try / catch

match registry.continue_agent(&handle, agent_ref, text, runtime) {
    Err(e) if e.to_string().contains("outside the active session") => {
        // spawn a fresh child in this session instead
        registry.spawn_child_with_context(&text)?;
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling continue/follow-up for an agent whose `resume_targets` successor has an `owner_session_id` different from the source's owner — e.g. resuming an agent carried over from a previous session — or when the source's owner_session_id is empty.

Common situations: Restoring persisted subagent state into a new session and continuing an old lineage; passing an agent id from a detached/foreign session to the active registry; records loaded without owner metadata (empty owner string).

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/c9a8d0dc08cefcec. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/subagent/mod.rs:6004

    /// 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,
        active_session_id: &str,
        agent_ref: &str,

View on GitHub (pinned to 73e0f67d83)