Hmbown/CodeWhale · error
invalid Runtime Chat
Error message
invalid Runtime Chat {label} What it means
validate_relay_id is the shared identifier validator for Runtime Chat relay ids (run id, route labels, operation keys, etc.). It rejects values that are empty, too long, contain "://", or contain any byte outside the allowed ASCII set (alphanumeric plus . _ : @ / + ~ -). The bail includes the caller-provided label, e.g. "invalid Runtime Chat run id".
Solutions
- Strip the scheme from URLs and pass only the bare id portion (remove "://...")
- Replace disallowed characters with allowed ones (. _ : @ / + ~ - or alphanumeric)
- Check that the value is non-empty before validating
- Restrict upstream input to ASCII identifiers (e.g. a regex like ^[A-Za-z0-9._:@/+~-]+$)
Example fix
// before let id = "https://example.com/run/abc"; // contains "://" // after let id = "example.com/run/abc";
Defensive patterns
Strategy: validation
Validate before calling
const RELAY_ID_RE: once_cell::sync::Lazy<regex::Regex> = once_cell::sync::Lazy::new(
|| regex::Regex::new(r"^[A-Za-z0-9._:@/+~-]+$").unwrap(),
);
fn is_valid_relay_id(value: &str) -> bool {
!value.is_empty() && !value.contains("://") && RELAY_ID_RE.is_match(value)
} Type guard
fn as_relay_id(value: &str) -> Option<&str> {
(!value.is_empty()
&& !value.contains("://")
&& value.bytes().all(|b| b.is_ascii_alphanumeric()
|| matches!(b, b'.' | b'_' | b':' | b'@' | b'/' | b'+' | b'~' | b'-')))
.then_some(value)
} Try / catch
match validate_relay_id(&id, "run id") {
Ok(()) => { /* proceed */ }
Err(e) => {
eprintln!("{e}: sanitizing id before use");
let id = sanitize_relay_id(&id); // strip scheme, replace bad chars
}
} Prevention
- Never pass full URLs where the API expects a bare relay id ("://" is explicitly rejected)
- Constrain user-supplied ids at input time to [A-Za-z0-9._:@/+~-]
- Sanitize provider-supplied identifiers before storing them in relay state
When it happens
Trigger: Any validate_relay_id(value, label) call (directly or via validate_operation_key, validate_relay_id wrappers in validate()) receiving a value that is empty, contains "://", or has disallowed characters (spaces, Unicode, control bytes, %, ?, &, etc.).
Common situations: Passing a full URL where a bare id is expected (the "://" check specifically catches this); user-supplied thread or route names with spaces or non-ASCII characters; ids with percent-encoding or query strings; empty strings from failed lookups.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Invalid preallocated task id: expected task_<16hex>
- invalid Runtime Chat virtual thread id
- 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/a4851b95b68090d8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_chat_relay.rs:1697
#[cfg(test)]
if take_state_persist_failure(path) {
bail!("injected Runtime Chat state persistence failure");
}
let body = serde_json::to_vec(state).context("encode Runtime Chat binding state")?;
crate::utils::write_atomic(path, &body).context("persist Runtime Chat binding state")
}
fn validate_relay_id(value: &str, label: &str) -> Result<()> {
if value.is_empty()
|| value.len() > MAX_RELAY_ID_BYTES
|| value.contains("..")
|| value.contains("://")
|| !value.bytes().all(|byte| {
byte.is_ascii_alphanumeric()
|| matches!(byte, b'.' | b'_' | b':' | b'@' | b'/' | b'+' | b'~' | b'-')
})
{
bail!("invalid Runtime Chat {label}");
}
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'))View on GitHub (pinned to 73e0f67d83)