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

  1. Check engine.get_run(run_id).status == SopRunStatus::PausedCheckpoint before resuming.
  2. Make resume idempotent in the caller: if the run is Running again or Completed, treat it as success/no-op.
  3. 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

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


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/98d306a9cd0e7018. Report an issue: GitHub.