{"record":{"id":"f434a11e58d2282a","repo":"zeroclaw-labs/zeroclaw","slug":"field-cannot-be-empty","errorCode":null,"errorMessage":"{field} cannot be empty","messagePattern":"(.+?) cannot be empty","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/sop/procedural_memory.rs","lineNumber":469,"sourceCode":"\nfn hash_sop_dir(dir: &Path) -> Result<String> {\n    let mut hasher = Sha256::new();\n    for name in [\"SOP.toml\", \"SOP.md\"] {\n        let path = dir.join(name);\n        hasher.update(name.as_bytes());\n        hasher.update([0]);\n        if path.exists() {\n            hasher.update(fs::read(path)?);\n        }\n        hasher.update([0]);\n    }\n    Ok(format!(\"sha256:{}\", hex::encode(hasher.finalize())))\n}\n\nfn require_nonempty<'a>(field: &str, value: &'a str) -> Result<&'a str> {\n    let trimmed = value.trim();\n    if trimmed.is_empty() {\n        bail!(\"{field} cannot be empty\");\n    }\n    Ok(trimmed)\n}\n\nfn safe_component(value: &str) -> String {\n    let out = value\n        .chars()\n        .map(|ch| {\n            if ch.is_ascii_alphanumeric() || ch == '-' || ch == '_' {\n                ch\n            } else {\n                '-'\n            }\n        })\n        .collect::<String>();\n    out.trim_matches('-').to_string()\n}\n","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/sop/procedural_memory.rs#L451-L487","documentation":"create_proposal validates three required text fields with require_nonempty, which trims and rejects blank values in a fixed order: sop_name first, then procedure_markdown, then description. The bail message names the offending field. This is the earliest validation in the proposal pipeline - it fires before leak scanning and candidate round-tripping.","triggerScenarios":"Calling sop::procedural_memory::create_proposal with a draft whose sop_name, procedure_markdown, or description is empty or whitespace-only (\"\", \"   \", \"\\n\"). The first blank field in the order sop_name -> procedure_markdown -> description is the one reported.","commonSituations":"UI/model flows that build ProposalDraft from optional form fields without defaults, or capture_successful_run operating on a SOP whose stored description is empty (the SOP loader allowed it but proposals do not).","solutions":["Populate all three fields with non-blank text before calling create_proposal; the reported field name tells you which one is blank","If the error comes from capture_successful_run, fix the source SOP's blank description in its manifest/SOP.toml and reload the engine","Trim inputs at the call site so accidental whitespace-only submissions are caught early"],"exampleFix":"// before\nlet draft = ProposalDraft {\n    sop_name: \"deploy-check\".into(),\n    description: \"   \".into(),\n    manifest_toml: None,\n    procedure_markdown: md,\n    source_run_id: None,\n    requested_by: None,\n};\n\n// after\nlet draft = ProposalDraft {\n    sop_name: \"deploy-check\".into(),\n    description: \"Verify the deploy finished\".into(),\n    manifest_toml: None,\n    procedure_markdown: md,\n    source_run_id: None,\n    requested_by: None,\n};","handlingStrategy":"validation","validationCode":"fn draft_is_complete(d: &ProposalDraft) -> bool {\n    [&d.sop_name, &d.procedure_markdown, &d.description]\n        .iter()\n        .all(|f| !f.trim().is_empty())\n}","typeGuard":null,"tryCatchPattern":"Err(e) if e.to_string().contains(\"cannot be empty\") => {\n    // the message names the field (sop_name | procedure_markdown | description):\n    // collect the missing field, prompt for it, resubmit the draft\n}","preventionTips":["Trim-and-check all three ProposalDraft text fields at the form/model boundary","Keep SOP descriptions mandatory in your authoring flow, since captured runs inherit them","Write unit tests asserting create_proposal rejects each blank field by name"],"tags":["sop","validation","required-field","procedural-memory"],"backgroundTag":"empty-required-field","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}