Hmbown/CodeWhale · error

Invalid preallocated task id: expected task_<16hex>

Error message

Invalid preallocated task id: expected task_<16hex>

What it means

Client-preallocated task ids must be exactly `task_` followed by 16 ASCII hex digits (21 chars total). This validator rejects any other shape before the id is used in file paths or admission, preventing malformed or unsafe ids from entering the store.

Solutions

  1. Generate the id as `task_` + 16 lowercase hex chars (e.g. from 8 random bytes)
  2. Trim/normalize the id and re-validate with the same rule before submitting
  3. Prefer letting the manager allocate the id instead of preallocating one

Example fix

// before
let id = format!("{}", uuid::Uuid::new_v4()); // wrong shape
// after
let id = format!("task_{}", hex::encode(rand::random::<[u8; 8]>())); // task_ + 16 hex
Defensive patterns

Strategy: validation

Validate before calling

fn valid_preallocated_id(id: &str) -> bool {
    id.len() == 21 && id.starts_with("task_") && id[5..].bytes().all(|b| b.is_ascii_hexdigit())
}

Type guard

fn as_task_id(s: &str) -> Option<&str> {
    (s.len() == 21 && s.starts_with("task_") && s[5..].bytes().all(|b| b.is_ascii_hexdigit())).then_some(s)
}

Try / catch

match manager.create_task_with_preallocated_id(&id, req).await {
    Err(e) if e.to_string().contains("Invalid preallocated task id") => {
        let id = format!("task_{}", hex::encode(rand::random::<[u8; 8]>()));
        manager.create_task_with_preallocated_id(&id, req).await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling task creation/admission with a client-supplied preallocated id that is not 21 chars, does not start with `task_`, or contains non-hex characters after the prefix.

Common situations: Generating ids with the wrong length (uuid strings, 8- or 12-char hex); forgetting the `task_` prefix; uppercase vs lowercase or stray whitespace; using a timestamp or numeric id instead of hex.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

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

            &self.queue_path,
            &QueueFile {
                queue: queue.iter().cloned().collect(),
            },
        )
    }

    fn persist_task_locked(&self, task: &TaskRecord) -> Result<()> {
        let path = self.tasks_dir.join(format!("{}.json", task.id));
        write_json_atomic(&path, task)
    }
}

fn validate_preallocated_task_id(task_id: &str) -> Result<()> {
    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>");
    }
    Ok(())
}

fn read_bound_task_file(path: &Path, task_id: &str) -> Result<Option<TaskRecord>> {
    let bytes = match fs::read(path) {
        Ok(bytes) => bytes,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None),
        Err(error) => return Err(error).context("read bound task"),
    };
    let task: TaskRecord = serde_json::from_slice(&bytes).context("decode bound task")?;
    if task.id != task_id || task.schema_version > CURRENT_TASK_SCHEMA_VERSION {
        bail!("Bound task identity or schema does not match its durable admission");
    }
    Ok(Some(task))
}

pub(crate) fn validate_bound_task_request(

View on GitHub (pinned to 73e0f67d83)