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
- Remove the credential-like text from the SOP/run output and re-capture; rotate any real credential that leaked into output.
- Fix the upstream steps to scrub or avoid printing secrets before their output is recorded.
- 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
- Never let steps print secret values; log the secret's name only.
- Scrub tool output before it is recorded as step results.
- Rotate any real credential that reaches procedural memory paths.
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
- proposal {} quarantined: {reason}
- proposal {} is {:?}, not pending
- proposal {} is stale; target SOP now exists
- proposal {} is stale; inspect and re-propose
- target SOP directory '{}' escapes SOPs root
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/d749de358d7c9c56.
Report an issue: GitHub.