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

SOP rejected: {}

Error message

SOP rejected: {}

What it means

save_sop clones the SOP, normalizes step numbers, then runs validate_sop_strict; if validation reports blocking findings they are joined with '; ' into this error and nothing is written to disk. Blocking findings include an empty SOP name, a step with an empty title, duplicate step numbers, and malformed step bindings or step-reference diagnostics.

Source

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

        }
        for bullet in render_step_bullets(step) {
            out.push_str(&format!("   - {bullet}\n"));
        }
    }
    out
}

/// Persist an SOP to `<sops_dir>/<name>/` as `SOP.toml` + `SOP.md`.
/// Normalizes step numbers first, then rejects the write entirely if
/// strict validation finds blocking problems; nothing touches disk on
/// failure.
pub fn save_sop(sops_dir: &Path, sop: &Sop) -> Result<()> {
    let mut sop = sop.clone();
    normalize_step_numbers(&mut sop);
    let sop = &sop;
    let validation = validate_sop_strict(sop);
    if !validation.is_ok() {
        anyhow::bail!("SOP rejected: {}", validation.blocking.join("; "));
    }

    let sop_dir = resolve_sop_dir(sops_dir, &sop.name)?;
    std::fs::create_dir_all(&sop_dir)?;

    let manifest = SopManifest::from_sop(sop);
    let toml_content = toml::to_string_pretty(&manifest)?;
    std::fs::write(sop_dir.join("SOP.toml"), toml_content)?;
    std::fs::write(sop_dir.join("SOP.md"), render_steps(&sop.steps))?;

    Ok(())
}

// ── Validation ──────────────────────────────────────────────────

/// Validate a loaded SOP and return a list of warnings.
pub fn validate_sop(sop: &Sop) -> Vec<String> {
    let mut warnings = Vec::new();

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the message: every blocking finding is listed; fix each one (titles non-empty, unique step numbers, well-formed bindings).
  2. Run validate_sop_strict(&sop) yourself before save to get findings without attempting a write.
  3. Dry-run save into a temp dir when validating authored content in tests or importers.
  4. Re-check numbering after hand edits, or let normalize_step_numbers handle sequencing by avoiding explicit conflicting numbers.

Example fix

// before: save fails with a joined list of blocking findings
save_sop(&sops_dir, &sop)?;

// after: validate first, surface each finding, only then save
let validation = validate_sop_strict(&sop);
if !validation.is_ok() {
    for issue in &validation.blocking {
        eprintln!("blocking: {issue}");
    }
    anyhow::bail!("SOP authoring failed validation");
}
save_sop(&sops_dir, &sop)?;
Defensive patterns

Strategy: validation

Validate before calling

let validation = validate_sop_strict(&sop);
if !validation.is_ok() {
    for issue in &validation.blocking {
        eprintln!("blocking: {issue}");
    }
    anyhow::bail!("SOP failed strict validation; nothing written");
}
save_sop(&sops_dir, &sop)?;

Try / catch

match save_sop(&sops_dir, &sop) {
    Err(e) if e.to_string().starts_with("SOP rejected:") => {
        // parse the '; '-joined findings and surface each authoring error
    }
    other => other?,
}

Prevention

When it happens

Trigger: Saving an SOP whose steps have empty titles, duplicate step numbers that normalization cannot disambiguate, malformed binding syntax, or broken step references; the bail happens after normalize_step_numbers but before any directory is created, so no partial SOP lands on disk.

Common situations: Hand-edited SOP.md files with repeated '1.' list markers; generated SOPs where a template field was left blank; refactoring steps and renumbering by hand; bindings referencing steps by an invalid form.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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