Hmbown/CodeWhale · error
{label} cannot be empty
Error message
{label} cannot be empty What it means
The runtime thread store validates every record id (thread ids, turn ids, item ids, owner ids) through validated_record_id. An id that trims to the empty string fails with '<label> cannot be empty'. Ids become filesystem path components, so blank ids are rejected before any path is built.
Source
Thrown at crates/tui/src/runtime_threads.rs:265
.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 0c42157ee5)
Solutions
- Generate ids with the store's own id generator (UUID-style) instead of deriving them from free-form input.
- Validate ids at the call site before invoking the store API (non-empty after trim).
- If the id comes from user input, reject blank submissions at the UI layer with a clear message.
Example fix
// before
store.load_thread("")?; // bails: thread id cannot be empty
// after
let id = require_nonempty(user_input)?;
store.load_thread(&id)?; Defensive patterns
Strategy: validation
Validate before calling
// Rust: mirror the store's rule before calling the API
fn is_valid_record_id(id: &str) -> bool {
!id.trim().is_empty()
&& id.trim() == id
&& id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
}
anyhow::ensure!(is_valid_record_id(thread_id), "invalid thread id {thread_id:?}");
store.load_thread(thread_id)?; Prevention
- Generate ids with UUID/hex generators instead of deriving them from user text.
- Centralize id validation in one helper used by every call site.
- Reject blank ids at the UI/input boundary with a descriptive message.
When it happens
Trigger: Calling store APIs such as load_thread(""), list_turns_for_thread(" "), or persisting a record whose thread_id/turn_id/owner_id is empty or whitespace-only. The label in the message tells you which id failed (e.g. 'thread id cannot be empty').
Common situations: Callers derive ids from user input or generated names that can be blank; a template or default produced an empty string; whitespace-only ids slip in from trimmed logs or copy-paste.
Related errors
- {label} cannot contain leading or trailing whitespace
- {label} contains unsupported characters
- Invalid preallocated task id: expected task_<16hex>
- ${name} is unavailable in Workflow scripts: runs must be det
- context_window must be greater than 0
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/a192c843412f73b0.
Report an issue: GitHub.