{"record":{"id":"b265c3c17a00021a","repo":"Hmbown/CodeWhale","slug":"api-key-id-must-be-key-id-len-lowercase-hex-characters-the","errorCode":null,"errorMessage":"API key id must be {KEY_ID_LEN} lowercase hex characters — the part after `cwc_key_` in the display prefix. Run `codewhale account api-keys list` to see them.","messagePattern":"API key id must be (.+?) lowercase hex characters — the part after `cwc_key_` in the display prefix\\. Run `codewhale account api-keys list` to see them\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/cli/src/cloud/machine.rs","lineNumber":855,"sourceCode":"            normalized.push(scope.to_string());\n        }\n    }\n    Ok(Some(normalized))\n}\n\n/// The 24-hex key id, checked locally.\n///\n/// This is a paste check, not an existence check: the server answers 404\n/// identically for a malformed id, an unknown id, and another account's id, so\n/// nothing here can or should try to distinguish them.\npub(crate) fn validate_key_id(id: &str) -> Result<&str> {\n    let id = id.trim();\n    if id.len() != KEY_ID_LEN\n        || !id\n            .bytes()\n            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))\n    {\n        bail!(\n            \"API key id must be {KEY_ID_LEN} lowercase hex characters — the part after `cwc_key_` \\\nin the display prefix. Run `codewhale account api-keys list` to see them.\"\n        );\n    }\n    Ok(id)\n}\n\n/// Refuse a management command that would be authenticated by a machine key.\n///\n/// The server would answer 403 `api_key_route_denied`, but a local refusal is\n/// better: it never puts the credential on the wire, and it names the fix.\n/// This is the load-bearing rule of the whole design — a stolen key must not\n/// be able to bootstrap a successor that outlives the revocation of the key\n/// that was stolen — so the CLI states it rather than discovering it.\npub(crate) fn reject_machine_key_for_management(\n    machine: &MachineKeyEnv,\n    has_session: bool,\n) -> Result<()> {","sourceCodeStart":837,"sourceCodeEnd":873,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/cli/src/cloud/machine.rs#L837-L873","documentation":"The key-id argument must be exactly KEY_ID_LEN lowercase hex characters — the segment immediately after `cwc_key_` in the key's display prefix. The validator trims the input, checks length and hex-ness (digits and a-f only), and bails with instructions to find ids via `codewhale account api-keys list`.","triggerScenarios":"Passing a key id to a revoke/delete command that is the wrong length, contains uppercase hex (A–F), includes the whole key/prefix, or contains non-hex characters (crates/cli/src/cloud/machine.rs:855).","commonSituations":"Passing the full key token instead of just the id after `cwc_key_`, copying the id with surrounding whitespace or quotes, uppercase hex from a formatted display, or confusing the key id with a key label.","solutions":["Run `codewhale account api-keys list` and copy the id exactly as shown","Take only the substring after `cwc_key_` (the 24-hex segment), not the whole key","Lowercase any uppercase hex characters","Trim whitespace and strip surrounding quotes"],"exampleFix":"// before\ncodewhale account api-keys revoke cwc_key_A1B2C3D4E5F60718293A4B5C\n// after\ncodewhale account api-keys revoke a1b2c3d4e5f60718293a4b5c","handlingStrategy":"validation","validationCode":"fn key_id_ok(id: &str, len: usize) -> bool {\n    let id = id.trim();\n    id.len() == len && id.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Copy ids directly from `codewhale account api-keys list`, never from a full key token","Remember the id is only the 24-hex segment after `cwc_key_`","Lowercase hex characters before passing them"],"tags":["cli","api-key","hex","validation"],"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-22T06:17:15.046Z"}