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
- 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.
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
- 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.
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
- Cannot start SOP '{}': cooldown or concurrency limit reached
- SOP '{sop_name}' not started: {reason}
- rename_agent not supported by this memory backend
- amqp.{}: dispatch = {:?} routes to the SOP engine but no SOP
- Email OAuth2 profile is missing token set: {profile_id}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/82757acf66bc6dc8.
Report an issue: GitHub.