{"record":{"id":"8bd57b72c0b4cb21","repo":"Hmbown/CodeWhale","slug":"operation-key-cannot-be-empty","errorCode":null,"errorMessage":"operation_key cannot be empty","messagePattern":"operation_key cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":3499,"sourceCode":"struct PreparedRuntimeTurnOperation {\n    binding: RuntimeTurnOperationBinding,\n    requested_turn_id: Option<String>,\n}\n\n/// Lookup errors deliberately omit operation keys and persisted file paths.\n#[derive(Debug, thiserror::Error)]\npub(crate) enum RuntimeTurnOperationLookupError {\n    #[error(\"Invalid thread id or operation key\")]\n    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(())","sourceCodeStart":3481,"sourceCodeEnd":3517,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L3481-L3517","documentation":"validate_runtime_turn_operation_key enforces that an operation key is a non-empty, trimmed UTF-8 string of at most MAX_RUNTIME_TURN_OPERATION_KEY_BYTES (128) bytes. An empty string fails immediately. The key names the exclusive turn operation being claimed, so an empty key cannot address anything.","triggerScenarios":"Calling the claim/lookup APIs with an operation key of \"\" — typically a key built by concatenating fields where the distinguishing part (turn id, tool name) was empty or the format string lost its substitution.","commonSituations":"A formatting bug composing keys like format!(\"{}\", turn_id) with an unset turn_id; copying a key placeholder without filling it in; a tool returning an empty identifier that was used verbatim as the key.","solutions":["Fix the key construction so it embeds a real, non-empty turn/operation identifier.","Log or inspect the composed key before the claim call to catch empty components at the source.","If the caller legitimately has no id yet, generate one (e.g., uuid) instead of passing an empty string.","Add an early assertion in the composing code that the key is non-empty before invoking the store."],"exampleFix":"// before\nlet key = format!(\"turn-{}-{}\", op.kind, op.id); // op.id empty -> key still invalid-ish or empty\nstore.claim(key)?; // \"operation_key cannot be empty\"\n\n// after\nlet id = if op.id.is_empty() { uuid::Uuid::new_v4().to_string() } else { op.id.clone() };\nlet key = format!(\"turn-{}-{}\", op.kind, id);\nassert!(!key.trim().is_empty());\nstore.claim(key)?;","handlingStrategy":"validation","validationCode":"fn valid_op_key(key: &str) -> bool {\n    !key.is_empty()\n        && key.len() <= 128\n        && key.trim() == key\n}\nif !valid_op_key(&key) { anyhow::bail!(\"refusing to claim with invalid operation_key\"); }","typeGuard":"fn non_empty_key(key: &str) -> Option<&str> {\n    (!key.is_empty() && key.trim() == key).then_some(key)\n}","tryCatchPattern":"match store.claim_turn_op(&key) {\n    Ok(c) => c,\n    Err(e) if e.to_string().contains(\"cannot be empty\") => {\n        eprintln!(\"operation key was empty; check key construction\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Build operation keys with a single helper that asserts non-empty components.","Generate identifiers (uuid) instead of propagating empty ids into keys.","Log composed keys at debug level to catch empty substitutions early."],"tags":["validation","arguments","locking"],"backgroundTag":"empty-required-field","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"}