Hmbown/CodeWhale · error

Cannot resume agent {agent_id}: status is {} ({})

Error message

Cannot resume agent {agent_id}: status is {} ({})

What it means

resume_from_checkpoint_with_policy gates on a ResumePolicy: InterruptedOnly accepts only Interrupted agents, InterruptedOrCompleted also accepts Completed. When the agent's actual status does not satisfy the requested policy, the resume is refused with the status name and the policy description. The policy exists so that, for example, an interrupted-only resume path can never silently resurrect an already-completed agent.

Source

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

            claim,
            preserved_profile,
            child_route,
        ) = {
            let agent = self
                .agents
                .get(&agent_id)
                .ok_or_else(|| anyhow!("Agent {agent_id} not found"))?;
            let resumable = match policy {
                ResumePolicy::InterruptedOnly => {
                    matches!(agent.status, SubAgentStatus::Interrupted(_))
                }
                ResumePolicy::InterruptedOrCompleted => matches!(
                    agent.status,
                    SubAgentStatus::Interrupted(_) | SubAgentStatus::Completed
                ),
            };
            if !resumable {
                return Err(anyhow!(
                    "Cannot resume agent {agent_id}: status is {} ({})",
                    subagent_status_name(&agent.status),
                    policy.describe()
                ));
            }
            let checkpoint = agent
                .checkpoint
                .as_ref()
                .filter(|cp| cp.continuable && !cp.messages.is_empty())
                .ok_or_else(|| {
                    let continuable = agent.checkpoint.as_ref().is_some_and(|cp| cp.continuable);
                    let messages = agent
                        .checkpoint
                        .as_ref()
                        .map(|cp| cp.messages.len())
                        .unwrap_or(0);
                    anyhow!(
                        "Agent {agent_id} has no continuable checkpoint to resume from (continuable={continuable}, messages={messages})"

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Derive the policy from the agent's current status instead of hardcoding it (Interrupted -> InterruptedOnly; Completed -> InterruptedOrCompleted)
  2. Prefer the higher-level continue_child_from_user, which selects the correct policy per status
  3. Re-check status with get_result_by_ref right before resuming and treat a mismatch as a signal to re-plan, not to retry the same policy

Example fix

// before
let resumed = manager.resume_from_checkpoint_with_policy(
    handle, runtime, &id, &text, ResumePolicy::InterruptedOnly)?;

// after
let snap = manager.get_result_by_ref(&id)?;
let policy = match snap.status {
    SubAgentStatus::Interrupted(_) => ResumePolicy::InterruptedOnly,
    SubAgentStatus::Completed => ResumePolicy::InterruptedOrCompleted,
    other => anyhow::bail!("status {other:?} cannot resume"),
};
let resumed = manager.resume_from_checkpoint_with_policy(
    handle, runtime, &id, &text, policy)?;
Defensive patterns

Strategy: type-guard

Validate before calling

// Choose the policy from observed status instead of a constant.
let snap = manager.get_result_by_ref(&agent_id)?;
let policy = match snap.status {
    SubAgentStatus::Interrupted(_) => ResumePolicy::InterruptedOnly,
    SubAgentStatus::Completed => ResumePolicy::InterruptedOrCompleted,
    other => anyhow::bail!("status {other:?} has no resume policy"),
};

Type guard

fn policy_allows(policy: ResumePolicy, status: &SubAgentStatus) -> bool {
    match policy {
        ResumePolicy::InterruptedOnly =>
            matches!(status, SubAgentStatus::Interrupted(_)),
        ResumePolicy::InterruptedOrCompleted =>
            matches!(status, SubAgentStatus::Interrupted(_) | SubAgentStatus::Completed),
    }
}

Try / catch

Catch 'Cannot resume agent' and re-read status; if it transitioned (e.g., interrupted -> completed), recompute the policy and retry once — further mismatches mean the agent is dead.

Prevention

When it happens

Trigger: Calling resume with ResumePolicy::InterruptedOnly on a Completed agent; calling resume on Failed, Cancelled, or BudgetExhausted agents (no policy accepts them); a status race where the agent transitions (e.g., interrupted -> completed) between the caller's decision and the resume call.

Common situations: Orchestration code hardcoding InterruptedOnly for all resumes; user-initiated 'continue' racing an agent that finishes on its own; mixing up the interrupt-resume path with the completion-continuation path after an API change.

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@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/1b5f56bfced62653. Report an issue: GitHub.