Hmbown/CodeWhale · error · anyhow::Error

Task prompt cannot be empty

Error message

Task prompt cannot be empty

What it means

add_task_with_id rejects a NewTaskRequest whose prompt is empty after trimming. Tasks are meaningless without an instruction, so enqueue fails fast before any id is staged, queued, or persisted. The check applies to both the normal enqueue path and the model tool's register-before-work transaction that preallocates an id.

Source

Thrown at crates/tui/src/task_manager.rs:1231

    }

    /// Allocate the durable owner identity before queue insertion so callers
    /// can register graph spawn intent first.
    #[must_use]
    pub(crate) fn new_task_id() -> String {
        format!("task_{}", &Uuid::new_v4().simple().to_string()[..16])
    }

    /// Enqueue using a preallocated id. This is crate-visible only for the
    /// model tool's register-before-work transaction.
    pub(crate) async fn add_task_with_id(
        &self,
        req: NewTaskRequest,
        task_id: String,
    ) -> Result<TaskRecord> {
        let prompt = req.prompt.trim().to_string();
        if prompt.is_empty() {
            bail!("Task prompt cannot be empty");
        }
        if task_id.len() != 21
            || !task_id.starts_with("task_")
            || !task_id[5..].chars().all(|ch| ch.is_ascii_hexdigit())
        {
            bail!("Invalid preallocated task id: expected task_<16hex>");
        }

        let task = TaskRecord {
            schema_version: CURRENT_TASK_SCHEMA_VERSION,
            // 16 random hex chars (was 8; ~60 bits of entropy once UUIDv4's
            // fixed version nibble is discounted): task ids live in durable
            // state that accumulates across restarts, and a collision
            // overwrites a record while leaving a duplicate queue entry.
            // `resolve_task_id` matches by prefix, so short references still
            // work.
            id: task_id,
            prompt,

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Validate and reject empty prompts at the call site before constructing NewTaskRequest.
  2. If the prompt comes from user or LLM input, require a non-blank default or re-prompt instead of enqueueing.
  3. Check req.prompt.trim() explicitly, not just is_empty(), because whitespace-only prompts are also rejected.

Example fix

// before
let task = tm.add_task_with_id(NewTaskRequest { prompt: prompt_from_user.clone(), .. }, id).await?;

// after
let prompt = prompt_from_user.trim();
if prompt.is_empty() {
    return Err(anyhow!("prompt required to create a task"));
}
let task = tm.add_task_with_id(NewTaskRequest { prompt: prompt.to_string(), .. }, id).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_prompt(req: &NewTaskRequest) -> bool {
    !req.prompt.trim().is_empty()
}

Prevention

When it happens

Trigger: Calling add_task_with_id (or the model task tool that fronts it) with a prompt of "", whitespace, or a value that only contains newlines/tabs; programmatic callers forwarding user input that was never validated.

Common situations: Automation or agent pipelines that build NewTaskRequest from optional template variables; trimming input upstream and passing the trimmed-empty string; tests with placeholder prompts.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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