zeroclaw-labs/zeroclaw · error · anyhow::Error
SOP '{}' already exists
Error message
SOP '{}' already exists What it means
create_sop deliberately refuses to overwrite: it checks whether the resolved SOP directory already exists and bails before delegating to save_sop. This is the create-or-refuse counterpart to save_sop, which overwrites unconditionally after validation.
Source
Thrown at crates/zeroclaw-runtime/src/sop/mod.rs:276
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 {
AlreadyExists(String),
NotFound(String),
Other(anyhow::Error),
}
impl std::fmt::Display for SopAuthorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SopAuthorError::AlreadyExists(name) => write!(f, "SOP '{name}' already exists"),
SopAuthorError::NotFound(name) => write!(f, "SOP '{name}' not found"),View on GitHub (pinned to 88bb9c8533)
Solutions
- If overwriting is intended, call save_sop directly (it validates then overwrites).
- Otherwise pick a distinct name/versioned slug and create under that name.
- Check existence first and branch: skip creation when the SOP is already present.
Example fix
// before: bootstrap fails on second run
create_sop(&sops_dir, &sop)?;
// after: create only when absent, refresh otherwise
if !resolve_sop_dir(&sops_dir, &sop.name)?.exists() {
create_sop(&sops_dir, &sop)?;
} else {
save_sop(&sops_dir, &sop)?; // validate + overwrite
} Defensive patterns
Strategy: validation
Validate before calling
if resolve_sop_dir(&sops_dir, &sop.name)?.exists() {
// decide explicitly: refresh via save_sop, or bail with context
save_sop(&sops_dir, &sop)?;
} else {
create_sop(&sops_dir, &sop)?;
} Try / catch
match create_sop(&sops_dir, &sop) {
Err(e) if e.to_string().contains("already exists") => {
// pick a new name, or intentionally overwrite via save_sop
}
other => other?,
} Prevention
- Make bootstrap scripts check existence before creating.
- Use versioned slugs when re-importing packs.
- Reserve create_sop for first install; use save_sop for updates.
When it happens
Trigger: Calling create_sop with a name that already has a directory under sops_dir: re-running a bootstrap script, provisioning twice, or creating an SOP whose name collides with an existing one.
Common situations: Setup scripts that run repeatedly; two teams authoring SOPs with the same slug; re-importing an SOP pack that was already installed.
Related errors
- amqp.{}: dispatch = {:?} routes to the SOP engine but no SOP
- cannot rename agent memory to `{to}`: an existing memory sto
- Destination skill already exists: {}
- cannot record checkpoint approval vote for run {run_id}: its
- run {run_id} is parked at an approval gate; amend/revise app
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/0702f3712c1e0a99.
Report an issue: GitHub.