{"record":{"id":"8c37fedc036eb4c5","repo":"Hmbown/CodeWhale","slug":"invalid-runtime-chat-model-id","errorCode":null,"errorMessage":"invalid Runtime Chat model id","messagePattern":"invalid Runtime Chat model id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_chat_relay.rs","lineNumber":1755,"sourceCode":"        || !value\n            .bytes()\n            .all(|byte| byte.is_ascii_alphanumeric() || byte == b'_')\n    {\n        bail!(\"invalid {label}\");\n    }\n    Ok(())\n}\n\nfn validate_route_id(value: &str, label: &str) -> Result<()> {\n    if !crate::runtime_api::runtime_chat_route_id_is_safe(value) {\n        bail!(\"invalid Runtime Chat {label}\");\n    }\n    Ok(())\n}\n\nfn validate_model_id(value: &str) -> Result<()> {\n    if !crate::runtime_api::runtime_chat_model_id_is_safe(value) {\n        bail!(\"invalid Runtime Chat model id\");\n    }\n    Ok(())\n}\n\nfn validate_fingerprint(value: &str) -> Result<()> {\n    if value.len() != 64 || !value.bytes().all(|byte| byte.is_ascii_hexdigit()) {\n        bail!(\"invalid Runtime Chat fingerprint\");\n    }\n    Ok(())\n}\n\nfn fingerprint(value: &str) -> String {\n    hex_digest(Sha256::digest(value.as_bytes()))\n}\n\nfn runtime_chat_request_fingerprint(command: &RuntimeChatPrompt) -> Result<String, String> {\n    serde_json::to_value(command)\n        .map(|value| canonical_json_value(&value))","sourceCodeStart":1737,"sourceCodeEnd":1773,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_chat_relay.rs#L1737-L1773","documentation":"`validate_model_id` checks Runtime Chat model identifiers against `runtime_chat_model_id_is_safe` before the relay stores or sends them. A model id with characters outside the safe set is rejected to keep persisted records and requests well-formed.","triggerScenarios":"Calling a Runtime Chat relay API with a model id string that fails the safety predicate (unsafe characters, separators, injection-prone content).","commonSituations":"Copy-pasting a model name with slashes or whitespace from vendor docs; composing model ids dynamically from untrusted provider metadata.","solutions":["Verify the model id matches the safe format (typically `provider/model` allowed only if the predicate accepts it; otherwise use the canonical id).","Use the model id exactly as returned by the model registry instead of hand-editing it.","Normalize/trim the id before passing it in."],"exampleFix":"// before\nlet model = \" openai/gpt-4 \";\nrelay.validate_model_id(model)?;\n// after\nlet model = \" openai/gpt-4 \".trim();\nrelay.validate_model_id(model)?; // or use the registry's canonical id","handlingStrategy":"validation","validationCode":"fn is_safe_model_id(id: &str) -> bool { !id.is_empty() && id == id.trim() && id.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_' | '.' | '/')) }\nif !is_safe_model_id(&model_id) { return Err(format!(\"model id not safe: {model_id}\")); }","typeGuard":"fn safe_model_id(id: &str) -> Option<&str> { (!id.is_empty() && id.trim() == id).then_some(id) }","tryCatchPattern":"match relay.validate_model_id(&model_id) { Err(e) => { eprintln!(\"{e}; using registry id instead\"); use_registry_model_id() }, Ok(_) => proceed() }","preventionTips":["Use model ids exactly as returned by the model registry","Trim and normalize ids sourced from config or docs","Validate model ids at config load time"],"tags":["validation","model","rust"],"backgroundTag":"invalid-identifier-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}