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

proposal rejected: {reason}

Error message

proposal rejected: {reason}

What it means

Before writing any proposal, create_proposal concatenates the manifest TOML and procedure markdown and runs them through a LeakDetector (scan_candidate). If credential-like content is detected (the message lists the matched patterns), the proposal is rejected outright and nothing is persisted. This keeps procedural memory from becoming a store of harvested secrets.

Source

Thrown at crates/zeroclaw-runtime/src/sop/procedural_memory.rs:47

    pub target_dir: PathBuf,
}

pub fn create_proposal(engine: &SopEngine, draft: ProposalDraft) -> Result<ProposalRecord> {
    let sop_name = require_nonempty("sop_name", &draft.sop_name)?;
    let procedure_markdown = require_nonempty("procedure_markdown", &draft.procedure_markdown)?;
    let description = require_nonempty("description", &draft.description)?;
    let existing = engine.get_sop(sop_name);
    let kind = if existing.is_some() {
        ProposalKind::Update
    } else {
        ProposalKind::Create
    };
    let manifest_toml = match draft.manifest_toml {
        Some(toml) if !toml.trim().is_empty() => toml,
        _ => default_manifest_toml(sop_name, description),
    };
    if let Some(reason) = scan_candidate(&manifest_toml, procedure_markdown) {
        bail!("proposal rejected: {reason}");
    }
    validate_candidate(sop_name, &manifest_toml, procedure_markdown)?;
    let now = now_iso8601();
    let id = format!(
        "prop-{}-{}-{:08x}",
        slugify(sop_name),
        now.replace(':', "_"),
        rand::random::<u32>()
    );
    let target_content_hash = existing
        .and_then(|sop| sop.location.as_deref())
        .map(hash_sop_dir)
        .transpose()?;
    let proposal = ProposalRecord {
        id,
        kind,
        status: ProposalStatus::Pending,
        source_run_id: draft.source_run_id,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Remove the credential-like text from the SOP/run output and re-capture; rotate any real credential that leaked into output.
  2. Fix the upstream steps to scrub or avoid printing secrets before their output is recorded.
  3. Reword notes so secrets never enter step output in the first place (reference secret names, not values).

Example fix

# before: run note echoes a live token, capture is rejected
- Step 2 ok: uploaded using token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

# after: reference the secret by name only
- Step 2 ok: uploaded using credentials from DEPLOY_TOKEN (value not logged)
Defensive patterns

Strategy: validation

Validate before calling

// Best-effort pre-scan mirroring the detector: key=value pairs with long values.
fn looks_like_credential(text: &str) -> bool {
    for line in text.lines() {
        let l = line.to_ascii_lowercase();
        for key in ["token=", "key=", "secret=", "password="] {
            if let Some(pos) = l.find(key) {
                let value = &line[pos + key.len()..];
                if value.chars().take_while(|c| c.is_ascii_graphic()).count() >= 20 {
                    return true;
                }
            }
        }
    }
    false
}

assert!(!looks_like_credential(&markdown), "scrub credential-like text before proposing");

Try / catch

match create_proposal(/* ... */) {
    Err(e) if e.to_string().contains("credential-like content detected") => {
        // find the flagged pattern, remove/rotate it, then re-capture
    }
    other => other?,
}

Prevention

When it happens

Trigger: Proposing (or capturing a successful run) whose manifest or procedure markdown contains credential-looking text: 'token=<20+ char value>' style pairs, API keys, or other detector patterns. Even scrubbed run notes can trip the generic-secret detector when long high-entropy values survive redaction.

Common situations: Step outputs that echo environment variables or auth headers; run notes quoting CLI output containing tokens; SOP descriptions embedding connection strings; test fixtures with realistic key material.

Related errors


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