{"record":{"id":"98d306a9cd0e7018","repo":"zeroclaw-labs/zeroclaw","slug":"run-is-not-paused-at-checkpoint-status","errorCode":null,"errorMessage":"Run {} is not paused at checkpoint (status: {})","messagePattern":"Run (.+?) is not paused at checkpoint \\(status: (.+?)\\)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/sop/engine.rs","lineNumber":4308,"sourceCode":"                \"sop_name\": sop_name,\n                \"step\": step.number,\n                \"kind\": step.kind.to_string(),\n            }),\n        );\n        self.finish_run(run_id, SopRunStatus::Failed, Some(reason))\n    }\n\n    /// Resume a deterministic run from persisted state.\n    pub fn resume_deterministic_run(\n        &mut self,\n        state: DeterministicRunState,\n    ) -> Result<SopRunAction> {\n        // Validate the run exists and is paused (immutable read), capturing its SOP\n        // name, before any mutation - so the fail-closed reacquire can run first.\n        let sop_name = match self.active_runs.get(&state.run_id) {\n            Some(run) if run.status == SopRunStatus::PausedCheckpoint => run.sop_name.clone(),\n            Some(run) => {\n                bail!(\n                    \"Run {} is not paused at checkpoint (status: {})\",\n                    state.run_id,\n                    run.status\n                );\n            }\n            None => {\n                let run_id = state.run_id.clone();\n                ::zeroclaw_log::record!(\n                    WARN,\n                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)\n                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                        .with_attrs(::serde_json::json!({\"run_id\": run_id})),\n                    \"SOP engine: active run not found\"\n                );\n                bail!(\"Active run not found: {}\", state.run_id);\n            }\n        };\n","sourceCodeStart":4290,"sourceCodeEnd":4326,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/sop/engine.rs#L4290-L4326","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before: blind resume that errors on an already-resumed run\nengine.resume_checkpoint(state).await?;\n\n// after: gate on live status\nif let Some(run) = engine.get_run(&state.run_id) {\n    if run.status != SopRunStatus::PausedCheckpoint {\n        tracing::info!(status = %run.status, \"run not parked; skip resume\");\n        return Ok(());\n    }\n}\nengine.resume_checkpoint(state).await?;","handlingStrategy":"validation","validationCode":"if let Some(run) = engine.get_run(&state.run_id) {\n    if run.status != SopRunStatus::PausedCheckpoint {\n        tracing::info!(status = %run.status, \"run not parked; skip resume\");\n        return Ok(());\n    }\n}\nengine.resume_checkpoint(state).await?;","typeGuard":null,"tryCatchPattern":"match engine.resume_checkpoint(state).await {\n    Err(e) if e.to_string().contains(\"is not paused at checkpoint\") => {\n        // someone else already moved the run; re-read status and converge\n    }\n    other => other?,\n}","preventionTips":["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."],"tags":["sop","checkpoint","resume","run-status"],"backgroundTag":"invalid-state-transition","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}