Hmbown/CodeWhale · error · anyhow::Error

{kind} must not be empty

Error message

{kind} must not be empty

What it means

ensure_safe_storage_id validates identifiers that become file names under the automation and task storage trees. It splits the value as a path and bails with '{kind} must not be empty' when the string yields zero path components — an empty id. The {kind} placeholder names the exact field at the call site: 'automation id', 'trigger id', 'run id', or 'task id'.

Source

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

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 0c42157ee5)

Solutions

  1. Populate the id before the call — generate a uuid or slug at record creation time.
  2. If the id derives from a user-entered name, validate the name is non-empty before building the record.
  3. Use the {kind} text in the message to identify exactly which field was empty.

Example fix

// before
let record = DelayedTriggerRecord {
    trigger_id: String::new(), // never populated
    // ..remaining fields
};
manager.save_trigger(&record)?; // bails: trigger id must not be empty

// after
let record = DelayedTriggerRecord {
    trigger_id: uuid::Uuid::new_v4().simple().to_string(),
    // ..remaining fields
};
manager.save_trigger(&record)?;
Defensive patterns

Strategy: validation

Validate before calling

fn non_empty_storage_id(id: &str) -> bool {
    !id.trim().is_empty()
}

Prevention

When it happens

Trigger: Saving or addressing an automation, trigger, run, or task with an empty id string — e.g. a record built with an uninitialized or trimmed-to-empty id passed to the save/get/delete entry points that call ensure_safe_storage_id (automation_manager.rs lines ~920-949).

Common situations: Struct literals with placeholder ids (String::new()) reaching a save path; ids derived from user-supplied names that were blank; an upstream id generator returning empty and the failure being swallowed before the store call.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/326abf7b2ca79599. Report an issue: GitHub.