zeroclaw-labs/zeroclaw · error · anyhow::Error
Run {} is not paused at checkpoint (status: {})
Error message
Run {} is not paused at checkpoint (status: {}) What it means
The resume-from-checkpoint path validates, as an immutable read before any mutation, that the run is in SopRunStatus::PausedCheckpoint (capturing its SOP name for the fail-closed reacquire). If the run exists but has any other status, resume bails with the actual status shown. This keeps the reacquire logic from running against a run that is not actually parked.
Source
Thrown at crates/zeroclaw-runtime/src/sop/engine.rs:4308
"sop_name": sop_name,
"step": step.number,
"kind": step.kind.to_string(),
}),
);
self.finish_run(run_id, SopRunStatus::Failed, Some(reason))
}
/// Resume a deterministic run from persisted state.
pub fn resume_deterministic_run(
&mut self,
state: DeterministicRunState,
) -> Result<SopRunAction> {
// Validate the run exists and is paused (immutable read), capturing its SOP
// name, before any mutation - so the fail-closed reacquire can run first.
let sop_name = match self.active_runs.get(&state.run_id) {
Some(run) if run.status == SopRunStatus::PausedCheckpoint => run.sop_name.clone(),
Some(run) => {
bail!(
"Run {} is not paused at checkpoint (status: {})",
state.run_id,
run.status
);
}
None => {
let run_id = state.run_id.clone();
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)
.with_attrs(::serde_json::json!({"run_id": run_id})),
"SOP engine: active run not found"
);
bail!("Active run not found: {}", state.run_id);
}
};
View on GitHub (pinned to 88bb9c8533)
Solutions
- Check engine.get_run(run_id).status == SopRunStatus::PausedCheckpoint before resuming.
- Make resume idempotent in the caller: if the run is Running again or Completed, treat it as success/no-op.
- Ensure only one handler owns a given parked run (dedupe by run id or checkpoint reference).
Example fix
// before: blind resume that errors on an already-resumed run
engine.resume_checkpoint(state).await?;
// after: gate on live status
if let Some(run) = engine.get_run(&state.run_id) {
if run.status != SopRunStatus::PausedCheckpoint {
tracing::info!(status = %run.status, "run not parked; skip resume");
return Ok(());
}
}
engine.resume_checkpoint(state).await?; Defensive patterns
Strategy: validation
Validate before calling
if let Some(run) = engine.get_run(&state.run_id) {
if run.status != SopRunStatus::PausedCheckpoint {
tracing::info!(status = %run.status, "run not parked; skip resume");
return Ok(());
}
}
engine.resume_checkpoint(state).await?; Try / catch
match engine.resume_checkpoint(state).await {
Err(e) if e.to_string().contains("is not paused at checkpoint") => {
// someone else already moved the run; re-read status and converge
}
other => other?,
} Prevention
- Ensure a single owner per parked run (dedupe resume triggers by run id).
- Treat 'not paused' on resume as a no-op success in automation.
- Log the returned status to spot racing resolvers quickly.
When it happens
Trigger: Calling resume on a run that already resumed (double-resume), completed, failed, or is currently executing; resuming after another handler already moved the run past the checkpoint.
Common situations: Duplicate resume deliveries from a queue or UI retry; two workers watching the same parked run; resume fired after an operator already resolved the checkpoint through a different path.
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
- Run {} cannot resume: its parked checkpoint snapshot is not
- cannot record checkpoint approval vote for run {run_id}: its
- Active run not found: {}
- amqp.{}: dispatch = {:?} routes to the SOP engine but no SOP
- QQ WebSocket connection closed: server requested reconnect (
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/98d306a9cd0e7018.
Report an issue: GitHub.