zeroclaw-labs/zeroclaw · info · anyhow::Error

SOP '{sop_name}' not started: coalesced into in-flight run {

Error message

SOP '{sop_name}' not started: coalesced into in-flight run {existing_run_id}

What it means

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.

Source

Thrown at crates/zeroclaw-runtime/src/sop/engine.rs:1577

                    "SOP dispatch: per-message dedup window evicted a still-active run's \
                     key (window full); a later redelivery of that message may re-run it"
                );
            }
        }
    }

    /// Start a new SOP run. Returns the first action to take.
    /// Deterministic SOPs are automatically routed to `start_deterministic_run`.
    /// Enforce the SOP's admission policy at a start entrypoint. `Admit` proceeds;
    /// any other outcome declines the start with a descriptive error so a trigger is
    /// never run past its policy. dispatch pre-consults `evaluate_admission` and only
    /// reaches a start path on `Admit`, so re-checking here (under the same held lock)
    /// is idempotent; a DIRECT caller (`sop_execute`, or `start_deterministic_run`)
    /// would otherwise bypass Hold / Coalesce / the `max_pending_approvals` pool.
    fn enforce_admission(&self, sop_name: &str) -> Result<()> {
        match self.evaluate_admission(sop_name) {
            SopAdmission::Admit => Ok(()),
            SopAdmission::Coalesce { existing_run_id } => bail!(
                "SOP '{sop_name}' not started: coalesced into in-flight run {existing_run_id}"
            ),
            SopAdmission::Defer { reason } | SopAdmission::Drop { reason } => {
                bail!("SOP '{sop_name}' not started: {reason}")
            }
        }
    }

    fn rollback_failed_start(
        &mut self,
        run_id: &str,
        claim: &ClaimToken,
        err: anyhow::Error,
    ) -> anyhow::Error {
        if err.is::<TerminalPersistenceRetained>() {
            return err;
        }
        self.active_runs.remove(run_id);

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Treat this as expected dedup behavior: follow the named existing_run_id (its output/result) instead of starting a new run.
  2. If you truly need a parallel run, change the SOP's admission_policy to Parallel (or wait for the in-flight run to complete).
  3. Route triggers through the dispatch API, which returns Coalesced as a first-class outcome rather than an error.
  4. Before manual starts, check for an active run of the same SOP.

Example fix

# before
zeroclaw sop execute deploy   # coalesce policy; run abc123 already in flight -> error

# after
zeroclaw runs get abc123        # follow the in-flight run named by the error
# or set admission_policy = "parallel" in the SOP if concurrent runs are intended
Defensive patterns

Strategy: fallback

Validate before calling

if let Some(existing) = engine.first_active_run_for_sop(sop_name) {
    return follow_run(existing); // do not start a duplicate
}
engine.start_sop(sop_name, input)?;

Try / catch

match engine.start_sop(sop_name, input).await {
    Err(e) if e.to_string().contains("coalesced into in-flight run") => {
        let existing = extract_run_id(&e); // parse existing_run_id from the message
        follow_run(existing).await        // success path: observe the in-flight run
    }
    rest => rest?,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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