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

SOP '{name}' not found

Error message

SOP '{name}' not found

What it means

delete_sop resolves the SOP's directory (with the same single-component validation as everything else) and refuses to delete when that directory does not exist. The SOP name may be misspelled, already deleted, or never created in this sops_dir.

Source

Thrown at crates/zeroclaw-runtime/src/sop/mod.rs:266

    load_sops_from_directory(&dir, default_execution_mode)
}

/// Load a single SOP by directory name from the SOPs root. Errors if the
/// directory or its `SOP.toml` is missing or malformed.
pub fn load_sop_by_name(
    sops_dir: &Path,
    name: &str,
    default_execution_mode: SopExecutionMode,
) -> Result<Sop> {
    load_sop(&resolve_sop_dir(sops_dir, name)?, default_execution_mode)
}

/// Delete an SOP's directory (manifest, steps, everything). Errors if no
/// SOP with that name exists.
pub fn delete_sop(sops_dir: &Path, name: &str) -> Result<()> {
    let dir = resolve_sop_dir(sops_dir, name)?;
    if !dir.exists() {
        anyhow::bail!("SOP '{name}' not found");
    }
    std::fs::remove_dir_all(&dir)?;
    Ok(())
}

/// Create a new SOP on disk, refusing to overwrite an existing one. Same
/// normalization and validation as `save_sop`.
pub fn create_sop(sops_dir: &Path, sop: &Sop) -> Result<()> {
    if resolve_sop_dir(sops_dir, &sop.name)?.exists() {
        anyhow::bail!("SOP '{}' already exists", sop.name);
    }
    save_sop(sops_dir, sop)
}

/// Typed classification of an authoring failure so transports map it to the
/// right status/RPC code without matching on stringified message substrings.
#[derive(Debug)]
pub enum SopAuthorError {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Check the SOP exists (list SOPs or check resolve_sop_dir(...).exists()) before deleting.
  2. Verify the exact name and the sops_dir root against the listing of loaded SOPs.
  3. For cleanup scripts, treat 'not found' as the desired end state and swallow that specific error.

Example fix

// before: blind delete in cleanup
delete_sop(&sops_dir, &name)?;

// after: idempotent delete
match delete_sop(&sops_dir, &name) {
    Ok(()) => {}
    Err(e) if e.to_string().contains("not found") => {
        tracing::debug!(%name, "SOP already gone");
    }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: validation

Validate before calling

if !resolve_sop_dir(&sops_dir, &name)?.exists() {
    tracing::debug!(%name, "SOP already absent");
    return Ok(());
}
delete_sop(&sops_dir, &name)?;

Try / catch

match delete_sop(&sops_dir, &name) {
    Err(e) if e.to_string().contains("not found") => Ok(()), // idempotent delete
    other => other,
}

Prevention

When it happens

Trigger: Calling delete_sop(sops_dir, name) after the SOP was already removed, with a typo in the name, or against the wrong sops_dir root (different install/config than where the SOP lives).

Common situations: Idempotent cleanup scripts that delete on every run; name casing or slug mismatches between create and delete; test teardown running against a fresh temp dir.

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/45d8b4761a6c8afc. Report an issue: GitHub.