{"record":{"id":"5fa88f0b14995182","repo":"Hmbown/CodeWhale","slug":"invalid-runtime-chat-fingerprint","errorCode":null,"errorMessage":"invalid Runtime Chat fingerprint","messagePattern":"invalid Runtime Chat fingerprint","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_chat_relay.rs","lineNumber":1762,"sourceCode":"}\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))\n        .and_then(|value| serde_json::to_vec(&value))\n        .map(|bytes| hex_digest(Sha256::digest(bytes)))\n        .map_err(|_| \"Runtime Chat could not fingerprint its validated request.\".to_string())\n}\n\nfn public_validation_error(_error: anyhow::Error) -> String {\n    \"The Runtime Chat command contains an invalid opaque identity.\".to_string()","sourceCodeStart":1744,"sourceCodeEnd":1780,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_chat_relay.rs#L1744-L1780","documentation":"`validate_fingerprint` requires a Runtime Chat fingerprint to be exactly 64 ASCII hex characters (a SHA-256 hex digest). Anything shorter, longer, or non-hex is rejected, because fingerprints are compared as digests for integrity.","triggerScenarios":"Passing a fingerprint that is not a 64-character hex string — e.g. a truncated hash, a base64 digest, or a value with `0x` prefix.","commonSituations":"Encoding the fingerprint differently than the producer (base64 vs hex); copying a hash with a prefix or whitespace; using a weaker hash like MD5 (32 hex chars).","solutions":["Provide the lowercase hex-encoded SHA-256 digest (exactly 64 hex chars, no `0x` prefix).","Recompute the fingerprint with the same hashing the relay uses (`Sha256::digest` + hex encoding).","Strip whitespace and prefixes before validating."],"exampleFix":"// before\nlet fp = format!(\"0x{}\", hex_digest(Sha256::digest(value)));\nrelay.validate_fingerprint(&fp)?;\n// after\nlet fp = hex_digest(Sha256::digest(value.as_bytes()));\nrelay.validate_fingerprint(&fp)?;","handlingStrategy":"validation","validationCode":"fn is_sha256_hex(s: &str) -> bool { s.len() == 64 && s.bytes().all(|b| b.is_ascii_hexdigit()) }\nif !is_sha256_hex(&fingerprint) { return Err(\"fingerprint must be 64 hex chars\".into()); }","typeGuard":"fn sha256_hex(s: &str) -> Option<&str> { (s.len() == 64 && s.bytes().all(|b| b.is_ascii_hexdigit())).then_some(s) }","tryCatchPattern":"match relay.validate_fingerprint(&fp) { Err(_) => { let fp = hex_digest(Sha256::digest(value.as_bytes())); retry_with(fp) }, Ok(_) => proceed() }","preventionTips":["Always hex-encode SHA-256 digests, never base64 or 0x-prefixed","Trim fingerprints read from config or environment","Agree on one encoding between producer and consumer"],"tags":["validation","hash","checksum"],"backgroundTag":"checksum-mismatch","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"}