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

Active run not found: {}

Error message

Active run not found: {}

What it means

The resume path could not find the run id in the engine's active_runs map, so there is nothing to resume. The engine logs a WARN Reject event with the run_id before bailing. Active runs are the in-memory execution surface (the durable store is the concurrency source of truth), so the id is unknown, expired, or was lost when the engine restarted.

Source

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

        let sop_name = match self.active_runs.get(&state.run_id) {
            Some(run) if run.status == SopRunStatus::PausedCheckpoint => run.sop_name.clone(),
            Some(run) => {
                bail!(
                    "Run {} is not paused at checkpoint (status: {})",
                    state.run_id,
                    run.status
                );
            }
            None => {
                let run_id = state.run_id.clone();
                ::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!({"run_id": run_id})),
                    "SOP engine: active run not found"
                );
                bail!("Active run not found: {}", state.run_id);
            }
        };

        // Refuse to resume while the checkpoint's parked snapshot has not yet
        // been durably persisted (see `is_park_persist_pending`'s doc): the kept
        // claim predates this attempt, and reacquiring on top of it would give a
        // later rollback or a maintenance retry no way to distinguish "freshly
        // reacquired" from "pre-existing, must survive."
        if self.is_park_persist_pending(&state.run_id) {
            bail!(
                "Run {} cannot resume: its parked checkpoint snapshot is not yet durably persisted (retrying)",
                state.run_id
            );
        }

        let sop = self
            .sops
            .iter()

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. List or query current runs to find the live run id before resuming.
  2. After an engine restart, re-admit/resume parked runs through the store-backed recovery path rather than replaying stale ids.
  3. Take the run id from the checkpoint notification itself, not from a stored or hand-typed copy.

Example fix

// before: resume with an id captured before an engine restart
engine.resume_checkpoint(state).await?; // state.run_id no longer active

// after: re-resolve the live run id first
let run_id = engine
    .list_runs()
    .into_iter()
    .find(|r| r.sop_name == state.sop_name && r.status == SopRunStatus::PausedCheckpoint)
    .map(|r| r.run_id)
    .ok_or_else(|| anyhow::anyhow!("no parked run for SOP {}", state.sop_name))?;
engine.resume_checkpoint(state.with_run_id(run_id)).await?;
Defensive patterns

Strategy: validation

Validate before calling

let Some(run) = engine.get_run(&state.run_id) else {
    // stale id (e.g. after engine restart): re-resolve the parked run from the live list
    anyhow::bail!("active run {} not found; re-resolve from list_runs", state.run_id);
};
engine.resume_checkpoint(state).await?;

Try / catch

match engine.resume_checkpoint(state).await {
    Err(e) if e.to_string().contains("Active run not found") => {
        // refresh the run id from the engine's live runs and retry once
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling resume with a run id that was never active in this process, was evicted after completion, or belonged to a previous engine instance before a restart; truncated or mistyped run ids from configuration or messages.

Common situations: Engine restart between checkpoint pause and resume; long-lived cron or channel handlers holding stale run ids; copy-pasting an id from old logs.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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