{"record":{"id":"b09ef7775a9cc793","repo":"Hmbown/CodeWhale","slug":"api-key-contains-invalid-control-characters","errorCode":null,"errorMessage":"API key contains invalid control characters","messagePattern":"API key contains invalid control characters","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/cloud.rs","lineNumber":884,"sourceCode":"\nfn validate_device_code(code: &str) -> Result<()> {\n    if code.len() != 43\n        || !code\n            .bytes()\n            .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_'))\n    {\n        bail!(\"The Codewhale service returned an invalid device authorization response\");\n    }\n    Ok(())\n}\n\nfn validate_api_key(key: &str) -> Result<()> {\n    let bytes = key.len();\n    if bytes < MIN_API_KEY_BYTES || bytes as u64 > MAX_API_KEY_BYTES {\n        bail!(\"API key must be {MIN_API_KEY_BYTES}-{MAX_API_KEY_BYTES} UTF-8 bytes\");\n    }\n    if key.chars().any(is_ascii_control) {\n        bail!(\"API key contains invalid control characters\");\n    }\n    Ok(())\n}\n\nfn validate_label(label: &str) -> Result<String> {\n    let label = label.split_whitespace().collect::<Vec<_>>().join(\" \");\n    if label.is_empty()\n        || label.chars().count() > MAX_KEY_LABEL_CHARS\n        || label.chars().any(is_ascii_control)\n    {\n        bail!(\"key label must contain 1-{MAX_KEY_LABEL_CHARS} characters\");\n    }\n    Ok(label)\n}\n\nfn is_ascii_control(character: char) -> bool {\n    character <= '\\u{001f}' || character == '\\u{007f}'\n}","sourceCodeStart":866,"sourceCodeEnd":902,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/cli/src/cloud.rs#L866-L902","documentation":"After the length check, the CLI rejects any API key containing ASCII control characters (code points <= 0x1f or 0x7f DEL). This bail fires when the key value embeds newlines, carriage returns, tabs, NULs, or escape bytes - usually the residue of how the value was stored or transported rather than the key itself.","triggerScenarios":"A key passed via --api-key-stdin or a prompt containing an embedded \\n or \\r; an env var set with a literal escape sequence; a config file value with a stray tab; binary garbage pasted into the prompt.","commonSituations":"Windows CRLF line endings when piping key files; secrets managers that store multi-line values; shell quoting that preserves \\n escape sequences instead of newlines being trimmed (leading/trailing are trimmed, interior ones are not); terminal paste glitches.","solutions":["Strip interior control bytes before storing: tr -d '\\r\\n\\000' < key.txt","Fix the env var or config entry to contain the single-line key with no escapes","Re-paste the key manually in the hidden prompt to eliminate paste artifacts","If a secrets manager wraps keys, extract the raw token field instead of the wrapper"],"exampleFix":"# before\nprintf 'sk-abc\\nsk-def' | codewhale cloud login --api-key-stdin   # interior newline\n# after\nprintf '%s' \"$(tr -d '\\r\\n' < key.txt)\" | codewhale cloud login --api-key-stdin","handlingStrategy":"validation","validationCode":"fn has_no_control_chars(s: &str) -> bool {\n    !s.chars().any(|c| c <= '\\u{001f}' || c == '\\u{007f}')\n}","typeGuard":"fn is_sanitized_api_key(key: &str) -> bool {\n    (8..=4096).contains(&key.trim().len())\n        && !key.trim().chars().any(|c| c <= '\\u{001f}' || c == '\\u{007f}')\n}","tryCatchPattern":null,"preventionTips":["tr -d '\\r\\n\\000' any key material piped from files on any OS","Avoid multi-line secrets wrappers; store the bare token","Assert single-line keys in provisioning scripts before use"],"tags":["cloud","api-key","validation","sanitization"],"backgroundTag":"api-key-validation-failed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}