Hmbown/CodeWhale · error · anyhow::Error

must not be empty

Error message

{kind} must not be empty

What it means

ensure_safe_storage_id validates that a storage identifier (kind describes which field, e.g. automation id or run id) is usable as a filename. An empty string yields no first path component, so it is rejected with this error before it can produce an invalid or colliding storage path.

Solutions

  1. Ensure the id is generated/non-empty before calling the storage API (e.g. reject empty input in the caller or use a UUID).
  2. Fix the form/deserialization path that produced an empty id.
  3. Add a caller-side check that !id.is_empty() before persisting.

Example fix

// before
store.save_run("")?;
// after
if run_id.is_empty() { return Err(anyhow!("run id must not be empty")); }
store.save_run(run_id)?;
Defensive patterns

Strategy: validation

Validate before calling

if id.is_empty() || Path::new(id).components().next().is_none() {
    return Err(anyhow!("{kind} must not be empty"));
}

Type guard

fn is_safe_storage_id(id: &str) -> bool {
    let mut c = Path::new(id).components();
    matches!(c.next(), Some(std::path::Component::Normal(_))) && c.next().is_none()
}

Try / catch

match ensure_safe_storage_id("run id", &id) {
    Ok(()) => store.save_run(&id, run)?,
    Err(e) => regenerate_id_and_retry(),
}

Prevention

When it happens

Trigger: Passing an empty string (or a value that Path::new sees as having no Normal components, like "" ) as an id to a save/load automation or run API.

Common situations: An id field left unset/empty in a deserialized record or UI form; trimming a name down to nothing before deriving an id; a generator bug producing empty ids.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/326abf7b2ca79599. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/automation_manager.rs:2777

fn read_run_file(path: &Path) -> Result<AutomationRunRecord> {
    let raw =
        fs::read_to_string(path).with_context(|| format!("Failed to read {}", path.display()))?;
    let run: AutomationRunRecord = serde_json::from_str(&raw)
        .with_context(|| format!("Failed to parse {}", path.display()))?;
    if run.schema_version > CURRENT_RUN_SCHEMA_VERSION {
        bail!(
            "Automation run schema v{} is newer than supported v{}",
            run.schema_version,
            CURRENT_RUN_SCHEMA_VERSION
        );
    }
    Ok(run)
}

fn ensure_safe_storage_id(kind: &str, value: &str) -> Result<()> {
    let mut components = Path::new(value).components();
    let Some(component) = components.next() else {
        bail!("{kind} must not be empty");
    };
    if components.next().is_some() || !matches!(component, std::path::Component::Normal(_)) {
        bail!("{kind} must be a single path component");
    }
    Ok(())
}

fn validate_name_and_prompt(name: &str, prompt: &str) -> Result<()> {
    if name.trim().is_empty() {
        bail!("Automation name is required");
    }
    if prompt.trim().is_empty() {
        bail!("Automation prompt is required");
    }
    Ok(())
}

fn normalize_optional_string(value: Option<String>) -> Option<String> {

View on GitHub (pinned to 73e0f67d83)