Hmbown/CodeWhale · error

cannot be empty

Error message

{label} cannot be empty

What it means

`validated_record_id` in the runtime threads store requires every record id (thread, turn, or item) to be a non-empty string before it is used to build a file path or a store query. An empty id would produce an invalid or dangerous path, so it is rejected.

Solutions

  1. Generate or assign the id before calling the store (e.g. a UUID or ULID).
  2. Check the id is non-empty at the call site before invoking the store API.
  3. Fix the deserialization so missing ids surface earlier instead of defaulting to an empty string.

Example fix

// before
store.save_turn(&thread_id, &turn_id, record)?; // turn_id == ""
// after
let turn_id = turn_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
store.save_turn(&thread_id, &turn_id, record)?;
Defensive patterns

Strategy: validation

Validate before calling

fn require_id(id: &str) -> Result<&str, String> { if id.trim().is_empty() { Err("record id is empty".into()) } else { Ok(id) } }

Type guard

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

Try / catch

match store.save_turn(thread_id, turn_id, record) { Err(e) if e.contains("cannot be empty") => { let id = uuid::Uuid::new_v4().to_string(); retry_with(id) }, Err(e) => return Err(e.into()), Ok(v) => v }

Prevention

When it happens

Trigger: Calling `record_path`, `save_turn`, `save_item`, `list_turns_for_thread`, `list_items_for_turn`, or `list_items_for_turns_map` with an id that is empty or all whitespace.

Common situations: A record was never assigned an id before saving; a lookup uses an unset/optional field defaulted to `""`; deserialized records with missing 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/a192c843412f73b0. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:297

        .find(COMPACTION_SUMMARY_END)
        .map(|rel| start + rel + COMPACTION_SUMMARY_END.len());
    let mut out = base[..start].trim_end().to_string();
    if let Some(end) = end {
        let tail = base[end..].trim_start();
        if !tail.is_empty() {
            if !out.is_empty() {
                out.push_str("\n\n");
            }
            out.push_str(tail);
        }
    }
    out
}

fn validated_record_id<'a>(id: &'a str, label: &str) -> Result<&'a str> {
    let trimmed = id.trim();
    if trimmed.is_empty() {
        bail!("{label} cannot be empty");
    }
    if trimmed != id {
        bail!("{label} cannot contain leading or trailing whitespace");
    }
    if !trimmed
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
    {
        bail!("{label} contains unsupported characters");
    }
    Ok(trimmed)
}

fn agent_mail_workspace_id(workspace: &Path) -> Result<String> {
    let canonical = workspace
        .canonicalize()
        .with_context(|| format!("resolve Agent Mail workspace {}", workspace.display()))?;
    let digest = Sha256::digest(canonical.to_string_lossy().as_bytes());

View on GitHub (pinned to 73e0f67d83)