Hmbown/CodeWhale · error
invalid Runtime Chat virtual thread id
Error message
invalid Runtime Chat virtual thread id
What it means
validate_virtual_thread_id requires virtual thread ids to be exactly 37 bytes, start with "local_thread_", and have a suffix of only ASCII digits and lowercase hex letters (a-f). These ids are locally generated; any other shape means the id was not produced by the local generator and is rejected.
Solutions
- Regenerate the id with the local virtual-thread-id generator instead of hand-building one
- Fix the prefix to "local_thread_" and pad the suffix to exactly 24 hex/digit chars
- Downcase hex digits and remove dashes/punctuation from the suffix
- Verify length: 13 (prefix) + 24 (suffix) = 37 characters
Example fix
// before let id = "local_thread_ABC-123"; // wrong length, uppercase, dashes // after let id = "local_thread_0123456789abcdef01234567"; // 13 + 24 = 37 chars
Defensive patterns
Strategy: validation
Validate before calling
fn looks_like_virtual_thread_id(value: &str) -> bool {
value.len() == 37
&& value.starts_with("local_thread_")
&& value[13..].bytes().all(|b| b.is_ascii_digit() || matches!(b, b'a'..=b'f'))
} Type guard
fn as_virtual_thread_id(value: &str) -> Option<&str> {
looks_like_virtual_thread_id(value).then_some(value)
} Try / catch
if !looks_like_virtual_thread_id(&thread_id) {
eprintln!("regenerating virtual thread id (got {thread_id:?})");
let thread_id = generate_virtual_thread_id();
} Prevention
- Only create virtual thread ids with the local generator; never accept external ids in this field
- Keep the 13+24=37 length invariant in a constructor function
- Keep hex lowercase; strip dashes if converting from UUID-like sources
When it happens
Trigger: Calling validate_virtual_thread_id (directly or via RelayState::validate over bindings) with an id that is not 37 chars, lacks the "local_thread_" prefix, or whose 24-char suffix contains uppercase hex, 'g'-'z', punctuation, or spaces.
Common situations: Constructing a virtual thread id by hand in tests or tooling with the wrong length or prefix; passing a server- or provider-side thread id (e.g. a UUID with dashes or uppercase) where a local id is required; truncation of a valid id.
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 preallocated task id: expected task_<16hex>
- invalid Runtime Chat
- invalid Runtime Chat virtual turn id
- cloud job id must look like cloud_
- CRON field contains an empty list item
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/5ce84429bee25927.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_chat_relay.rs:1717
Ok(())
}
fn validate_operation_key(value: &str) -> Result<()> {
validate_relay_id(value, "operation key")?;
if value.len() > MAX_OPERATION_KEY_BYTES {
bail!("Runtime Chat operation key is too long");
}
Ok(())
}
fn validate_virtual_thread_id(value: &str) -> Result<()> {
if value.len() != 37
|| !value.starts_with("local_thread_")
|| !value[13..]
.bytes()
.all(|byte| byte.is_ascii_digit() || matches!(byte, b'a'..=b'f'))
{
bail!("invalid Runtime Chat virtual thread id");
}
Ok(())
}
fn validate_virtual_turn_id(value: &str) -> Result<()> {
if value.len() != 35
|| !value.starts_with("local_turn_")
|| !value[11..]
.bytes()
.all(|byte| byte.is_ascii_digit() || matches!(byte, b'a'..=b'f'))
{
bail!("invalid Runtime Chat virtual turn id");
}
Ok(())
}
fn validate_native_record_id(value: &str, label: &str) -> Result<()> {
if value.len() < 5View on GitHub (pinned to 73e0f67d83)