{"record":{"id":"fbf4ea0f14791e5f","repo":"Hmbown/CodeWhale","slug":"automation-prompt-is-required","errorCode":null,"errorMessage":"Automation prompt is required","messagePattern":"Automation prompt is required","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/automation_manager.rs","lineNumber":1847,"sourceCode":"}\n\nfn ensure_safe_storage_id(kind: &str, value: &str) -> Result<()> {\n    let mut components = Path::new(value).components();\n    let Some(component) = components.next() else {\n        bail!(\"{kind} must not be empty\");\n    };\n    if components.next().is_some() || !matches!(component, std::path::Component::Normal(_)) {\n        bail!(\"{kind} must be a single path component\");\n    }\n    Ok(())\n}\n\nfn validate_name_and_prompt(name: &str, prompt: &str) -> Result<()> {\n    if name.trim().is_empty() {\n        bail!(\"Automation name is required\");\n    }\n    if prompt.trim().is_empty() {\n        bail!(\"Automation prompt is required\");\n    }\n    Ok(())\n}\n\nfn normalize_optional_string(value: Option<String>) -> Option<String> {\n    value\n        .map(|value| value.trim().to_string())\n        .filter(|value| !value.is_empty())\n}\n\nfn write_json_atomic<T: Serialize>(path: &Path, value: &T) -> Result<()> {\n    if let Some(parent) = path.parent() {\n        fs::create_dir_all(parent)\n            .with_context(|| format!(\"Failed to create {}\", parent.display()))?;\n    }\n    let content = serde_json::to_string_pretty(value)?;\n    let tmp = path.with_extension(\"json.tmp\");\n    fs::write(&tmp, content).with_context(|| format!(\"Failed to write {}\", tmp.display()))?;","sourceCodeStart":1829,"sourceCodeEnd":1865,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/automation_manager.rs#L1829-L1865","documentation":"The second half of validate_name_and_prompt: an automation's prompt must contain non-whitespace content after trimming. The prompt is the instruction the automation will execute, so a blank one is refused before the record is saved.","triggerScenarios":"Calling create_automation with req.prompt set to \"\" or whitespace-only — e.g. a template variable that expanded to nothing, or a textarea submitted empty.","commonSituations":"Prompt templates with unfilled placeholders; forms where the prompt step was skipped; copy-paste of invisible whitespace characters.","solutions":["Provide a real prompt describing what the automation should do.","Validate the prompt (trim + non-empty) at the form/CLI boundary.","If the prompt comes from a template, fail loudly when rendering produces only whitespace."],"exampleFix":"// before\nlet record = manager.create_automation(CreateAutomationRequest {\n    name: name.to_string(),\n    prompt: \"   \".to_string(), // blank after trim -> bails\n})?;\n\n// after\nlet prompt = rendered_prompt.trim();\nanyhow::ensure!(!prompt.is_empty(), \"Prompt cannot be blank\");\nlet record = manager.create_automation(CreateAutomationRequest {\n    name: name.to_string(),\n    prompt: prompt.to_string(),\n})?;","handlingStrategy":"validation","validationCode":"fn valid_automation_prompt(prompt: &str) -> bool {\n    !prompt.trim().is_empty()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fail loudly when prompt templates render to only whitespace.","Require non-empty prompt text in the UI before submit.","Keep pre-submit validation for name and prompt together so they cannot drift."],"tags":["rust","validation","automation","required-field"],"backgroundTag":"empty-required-field","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}