{"record":{"id":"af16b2f81a645afd","repo":"Hmbown/CodeWhale","slug":"operation-key-cannot-contain-control-characters","errorCode":null,"errorMessage":"operation_key cannot contain control characters","messagePattern":"operation_key cannot contain control characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":3508,"sourceCode":"    InvalidRequest,\n    #[error(\"Turn operation acceptance is incomplete; retry lookup\")]\n    Incomplete,\n    #[error(\"Turn operation lookup unavailable\")]\n    Unavailable,\n}\n\nfn validate_runtime_turn_operation_key(value: &str) -> Result<()> {\n    if value.is_empty() {\n        bail!(\"operation_key cannot be empty\");\n    }\n    if value.len() > MAX_RUNTIME_TURN_OPERATION_KEY_BYTES {\n        bail!(\"operation_key cannot exceed {MAX_RUNTIME_TURN_OPERATION_KEY_BYTES} UTF-8 bytes\");\n    }\n    if value.trim() != value {\n        bail!(\"operation_key cannot contain leading or trailing whitespace\");\n    }\n    if value.chars().any(char::is_control) {\n        bail!(\"operation_key cannot contain control characters\");\n    }\n    Ok(())\n}\n\nfn validate_sha256_fingerprint(value: &str, label: &str) -> Result<()> {\n    if value.len() != 64 || !value.bytes().all(|byte| byte.is_ascii_hexdigit()) {\n        bail!(\"{label} must be a SHA-256 hex digest\");\n    }\n    Ok(())\n}\n\nfn runtime_turn_operation_key_fingerprint(\n    owner_id: &str,\n    thread_id: &str,\n    operation_key: &str,\n) -> Result<String> {\n    validate_runtime_turn_operation_key(operation_key)?;\n    Ok(crate::hashing::sha256_hex(format!(","sourceCodeStart":3490,"sourceCodeEnd":3526,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L3490-L3526","documentation":"validate_runtime_turn_operation_key character check (after the empty, length, and trim guards): the key contains control characters, which would corrupt idempotency-key matching in the durable binding store, so such keys are rejected.","triggerScenarios":"Passing an operation_key containing chars like \\n, \\t, \\r, \\0, or other Unicode control codepoints, typically from binary data, raw process output, or multi-line heredocs.","commonSituations":"Building keys from command output or file contents that include newlines; programmatic key generation that accidentally joins with '\\n'.","solutions":["Sanitize the key: filter out or replace control chars before calling","Join multi-part data with a printable separator like ':' instead of newline","Escape/encode raw-derived content (hex or base64) before embedding it in the key"],"exampleFix":"// before\nlet key = raw_output; // may contain \\n\n// after\nlet key: String = raw_output.chars().filter(|c| !c.is_control()).collect();","handlingStrategy":"validation","validationCode":"let sanitized: String = key.chars().filter(|c| !c.is_control()).collect();\nassert_eq!(sanitized, key, \"operation_key must not contain control chars\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use printable separators (':') when joining key parts","Hex/base64-encode raw-derived content","Never pass process output or file bytes unescaped as a key"],"tags":["validation","encoding","runtime"],"backgroundTag":"invalid-argument-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}