zeroclaw-labs/zeroclaw · error · anyhow::Error
SOP '{}' is not in deterministic mode (mode: {})
Error message
SOP '{}' is not in deterministic mode (mode: {}) What it means
The deterministic direct-run entry only accepts SOPs whose execution_mode is SopExecutionMode::Deterministic. The mode is checked before an execution slot is reserved, so a wrong-mode direct call cannot claim (and then have to roll back) a slot. Any other mode (e.g. supervised/agent-driven) must go through the normal start-and-approve flow.
Source
Thrown at crates/zeroclaw-runtime/src/sop/engine.rs:3684
// / the pending-approval pool that `start_run` enforces. (When reached via
// `start_run` the re-check is idempotent under the same held lock.)
self.enforce_admission(sop_name)?;
let sop = self.get_sop(sop_name).ok_or_else(|| {
::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!({"sop_name": sop_name})),
"SOP engine: sop not found"
);
anyhow::Error::msg(format!("SOP not found: {sop_name}"))
})?;
// Reject a non-deterministic SOP BEFORE reserving a slot, so a wrong-mode direct
// call cannot claim (and then have to roll back) an execution slot.
if sop.execution_mode != SopExecutionMode::Deterministic {
bail!(
"SOP '{}' is not in deterministic mode (mode: {})",
sop_name,
sop.execution_mode
);
}
// Reserve + activate through the shared two-phase start path (identical run_id
// prefix, logging, and dispatch to the pre-refactor inline body).
let reservation = self.reserve_run_slot(sop_name)?;
self.activate_reserved_run(reservation, event)
}
pub fn drive_headless_deterministic(
&mut self,
run_id: &str,
first_action: SopRunAction,
) -> Result<SopRunAction> {
let mut action = first_action;View on GitHub (pinned to 88bb9c8533)
Solutions
- Use the normal SOP start/approval flow for non-deterministic SOPs instead of the deterministic direct entry.
- If unattended direct runs are intended, set execution_mode = "deterministic" in the SOP's SOP.toml [sop] section.
- Check engine.get_sop(name).execution_mode before calling, and route to the right entry based on the mode.
Example fix
# before - SOP.toml [sop] name = "nightly-backup" execution_mode = "supervised" # after - SOP is meant to run unattended via the deterministic entry [sop] name = "nightly-backup" execution_mode = "deterministic"
Defensive patterns
Strategy: validation
Validate before calling
let sop = engine
.get_sop(sop_name)
.ok_or_else(|| anyhow::anyhow!("SOP not found: {sop_name}"))?;
if sop.execution_mode != SopExecutionMode::Deterministic {
anyhow::bail!("use the start/approve flow for {}-mode SOPs", sop.execution_mode);
}
engine.run_deterministic(/* ... */).await?; Type guard
fn is_deterministic(sop: &Sop) -> bool {
sop.execution_mode == SopExecutionMode::Deterministic
} Try / catch
match engine.run_deterministic(/* ... */).await {
Err(e) if e.to_string().contains("is not in deterministic mode") => {
// route this SOP through the supervised start/approve flow instead
}
other => other?,
} Prevention
- Keep a registry mapping SOPs to their intended entry API based on execution_mode.
- Assert the mode in deploy scripts before scheduling unattended runs.
- Re-check execution_mode after anyone edits SOP.toml.
When it happens
Trigger: Calling the deterministic run entry with an SOP whose manifest declares a non-deterministic execution_mode (for example 'supervised'), or after someone edited SOP.toml to change the mode without updating callers.
Common situations: Automation scripts wired to the direct-run API while the SOP was authored for interactive approval; renaming or re-authoring an SOP with a different mode; copy-pasting a trigger/config from a deterministic SOP onto a supervised one.
Related errors
- amqp.{}: dispatch = {:?} routes to the SOP engine but no SOP
- cannot record checkpoint approval vote for run {run_id}: its
- run {run_id} is parked at an approval gate; amend/revise app
- cannot record approval vote for run {run_id}: its parked sna
- wait capability duration exceeds {MAX_WAIT_MS}ms
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/a306f8b3cf30be34.
Report an issue: GitHub.