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
- Check the SOP exists (list SOPs or check resolve_sop_dir(...).exists()) before deleting.
- Verify the exact name and the sops_dir root against the listing of loaded SOPs.
- 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
- Make teardown scripts idempotent: absence is the goal state.
- Confirm names against the loaded SOP list before deleting.
- Verify the sops_dir root matches the install that owns the SOP.
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
- Active run not found: {}
- SOP not found: {n}
- webhook-audit: {e}
- amqp.{}: dispatch = {:?} routes to the SOP engine but no SOP
- Call not found: {call_id}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/45d8b4761a6c8afc.
Report an issue: GitHub.