Hmbown/CodeWhale · error
Agent has no continuable checkpoint to resume from…
Error message
Agent {agent_id} has no continuable checkpoint to resume from (continuable={continuable}, messages={messages}) What it means
Resuming an interrupted sub-agent requires a stored checkpoint marked continuable. This error means the agent exists but its checkpoint is missing, non-continuable, or has no messages to replay, so the resume path fails closed rather than fabricating a fresh run.
Solutions
- Inspect the agent's checkpoint state (`continuable`, `messages.len()`) in the error text and confirm whether it should be continuable; if not, spawn a new sub-agent instead of resuming.
- Re-run the interrupted work as a fresh sub-agent spawn, carrying the original task prompt.
- If resuming mid-workflow, cancel or clean up the stale agent entry (agents/cancel or agents/remove) and re-dispatch.
- If checkpoints should persist across restarts, verify the session store is loading saved checkpoints and that no version change dropped them.
Example fix
// before
let out = agents.resume(agent_id)?;
// after
if let Some(agent) = agents.get(&agent_id) {
let continuable = agent.checkpoint.as_ref().is_some_and(|cp| cp.continuable && !cp.messages.is_empty());
if !continuable {
// respawn fresh instead of resuming
return spawn_new_agent(task);
}
}
let out = agents.resume(agent_id)?; Defensive patterns
Strategy: validation
Validate before calling
fn resumable(agent: &SubAgentHandle) -> bool {
agent.checkpoint.as_ref().is_some_and(|cp| cp.continuable && !cp.messages.is_empty())
}
// call only if resumable(&agent) Type guard
fn continuable_checkpoint(cp: &Option<Checkpoint>) -> Option<&Checkpoint> {
cp.as_ref().filter(|c| c.continuable && !c.messages.is_empty())
} Try / catch
match agents.resume(agent_id) {
Err(e) if e.to_string().contains("no continuable checkpoint") => spawn_fresh_agent(task),
other => other?,
} Prevention
- Check checkpoint.continuable before issuing resume
- Never assume an interrupted agent is always resumable; completed/cancelled ones are not
- Persist checkpoints across restarts and verify session-store loading
When it happens
Trigger: Calling the agent resume path (agents/resume) for an agent_id whose `agent.checkpoint` is None, or whose checkpoint has `continuable == false`, or whose checkpoint contains zero messages. The check runs before restoring the child's write claim.
Common situations: Resuming after the checkpoint was cleared by a completed/cancelled run; resuming a sub-agent that failed before producing any messages; double-resuming the same checkpoint; upgrading from an older session format that did not persist checkpoints.
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
- Cannot resume agent on its saved provider
- Cannot resume agent : sub-agent depth limit reached…
- sub-agent task must not park waiting for checkpoint input
- timeout should preserve checkpoint
- 1
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/eeef62884d0a4dde.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:6245
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})"
)
})?;
// Restore the interrupted child's write claim so the resumed loop
// stays inside the coordination ledger with the original bounded
// scope instead of inheriting the caller's unchecked write surface.
// The ledger claim is already namespaced and carries the isolation
// flag; both are passed through to the spawn seam.
let claim = self
.coordination
.write_claims
.iter()
.find(|record| record.claim.owner == agent_id)
.map(|record| (record.claim.clone(), record.isolated_worktree));
// Preserve the interrupted child's runtime posture (read_only /
// denied tools / shell) instead of rebuilding from the caller's
// role, which could widen the resumed child's authority.
let preserved_profile = selfView on GitHub (pinned to 73e0f67d83)