{"record":{"id":"82757acf66bc6dc8","repo":"zeroclaw-labs/zeroclaw","slug":"sop-sop-name-not-started-coalesced-into-in-fl","errorCode":null,"errorMessage":"SOP '{sop_name}' not started: coalesced into in-flight run {existing_run_id}","messagePattern":"SOP '(.+?)' not started: coalesced into in-flight run (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"info","filePath":"crates/zeroclaw-runtime/src/sop/engine.rs","lineNumber":1577,"sourceCode":"                    \"SOP dispatch: per-message dedup window evicted a still-active run's \\\n                     key (window full); a later redelivery of that message may re-run it\"\n                );\n            }\n        }\n    }\n\n    /// Start a new SOP run. Returns the first action to take.\n    /// Deterministic SOPs are automatically routed to `start_deterministic_run`.\n    /// Enforce the SOP's admission policy at a start entrypoint. `Admit` proceeds;\n    /// any other outcome declines the start with a descriptive error so a trigger is\n    /// never run past its policy. dispatch pre-consults `evaluate_admission` and only\n    /// reaches a start path on `Admit`, so re-checking here (under the same held lock)\n    /// is idempotent; a DIRECT caller (`sop_execute`, or `start_deterministic_run`)\n    /// would otherwise bypass Hold / Coalesce / the `max_pending_approvals` pool.\n    fn enforce_admission(&self, sop_name: &str) -> Result<()> {\n        match self.evaluate_admission(sop_name) {\n            SopAdmission::Admit => Ok(()),\n            SopAdmission::Coalesce { existing_run_id } => bail!(\n                \"SOP '{sop_name}' not started: coalesced into in-flight run {existing_run_id}\"\n            ),\n            SopAdmission::Defer { reason } | SopAdmission::Drop { reason } => {\n                bail!(\"SOP '{sop_name}' not started: {reason}\")\n            }\n        }\n    }\n\n    fn rollback_failed_start(\n        &mut self,\n        run_id: &str,\n        claim: &ClaimToken,\n        err: anyhow::Error,\n    ) -> anyhow::Error {\n        if err.is::<TerminalPersistenceRetained>() {\n            return err;\n        }\n        self.active_runs.remove(run_id);","sourceCodeStart":1559,"sourceCodeEnd":1595,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/sop/engine.rs#L1559-L1595","documentation":"enforce_admission() re-checks admission on direct start paths (sop_execute, start_deterministic_run) so they cannot bypass Hold/Coalesce/max_pending_approvals. With admission_policy = Coalesce and an active run for the SOP already in flight, evaluate_admission returns Coalesce and the direct start bails naming the existing run instead of silently starting a duplicate.","triggerScenarios":"Directly starting a SOP whose admission_policy is Coalesce while another run of the same SOP is executing or pending approval — e.g. a manual sop_execute landing on top of a dispatch-triggered run.","commonSituations":"Manual re-trigger while an automated run is in flight; retry logic that re-invokes the direct start API instead of following the coalesced run; monitoring that 'restarts' SOPs without checking for active runs.","solutions":["Treat this as expected dedup behavior: follow the named existing_run_id (its output/result) instead of starting a new run.","If you truly need a parallel run, change the SOP's admission_policy to Parallel (or wait for the in-flight run to complete).","Route triggers through the dispatch API, which returns Coalesced as a first-class outcome rather than an error.","Before manual starts, check for an active run of the same SOP."],"exampleFix":"# before\nzeroclaw sop execute deploy   # coalesce policy; run abc123 already in flight -> error\n\n# after\nzeroclaw runs get abc123        # follow the in-flight run named by the error\n# or set admission_policy = \"parallel\" in the SOP if concurrent runs are intended","handlingStrategy":"fallback","validationCode":"if let Some(existing) = engine.first_active_run_for_sop(sop_name) {\n    return follow_run(existing); // do not start a duplicate\n}\nengine.start_sop(sop_name, input)?;","typeGuard":null,"tryCatchPattern":"match engine.start_sop(sop_name, input).await {\n    Err(e) if e.to_string().contains(\"coalesced into in-flight run\") => {\n        let existing = extract_run_id(&e); // parse existing_run_id from the message\n        follow_run(existing).await        // success path: observe the in-flight run\n    }\n    rest => rest?,\n}","preventionTips":["Check for an active run of the same SOP before manual starts.","Treat 'coalesced into in-flight run' as a success-shaped outcome carrying a run id, not a failure.","Use the dispatch API for trigger sources — it returns Coalesced as a first-class result."],"tags":["sop","admission","coalesce","dedup","concurrency"],"backgroundTag":"request-coalesced","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}