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
- Generate the id as `task_` + 16 lowercase hex chars (e.g. from 8 random bytes)
- Trim/normalize the id and re-validate with the same rule before submitting
- 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
- Generate ids from a single shared helper that always emits task_<16hex>
- Validate ids at the client boundary before sending
- Never build ids from uuids, timestamps, or unprefixed hex
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
- invalid Runtime Chat
- invalid Runtime Chat virtual thread id
- invalid Runtime Chat virtual turn id
- Bound task identity or schema does not match its durable…
- cloud job id must look like cloud_
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)