{"record":{"id":"8fcdf553183fca25","repo":"Hmbown/CodeWhale","slug":"interactive-key-entry-requires-a-terminal-use","errorCode":null,"errorMessage":"interactive key entry requires a terminal; use `--api-key-stdin` for piped input","messagePattern":"interactive key entry requires a terminal; use `--api-key-stdin` for piped input","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/cli/src/cloud.rs","lineNumber":974,"sourceCode":"        .take(MAX_API_KEY_STDIN_BYTES + 1)\n        .read_to_end(&mut bytes)\n        .context(\"failed to read API key from stdin\")?;\n    parse_key_input(bytes)\n}\n\nfn parse_key_input(bytes: Vec<u8>) -> Result<String> {\n    if bytes.len() as u64 > MAX_API_KEY_STDIN_BYTES {\n        bail!(\"API key input is unexpectedly large\");\n    }\n    let value = String::from_utf8(bytes).context(\"API key from stdin is not valid UTF-8\")?;\n    let value = value.trim().to_string();\n    validate_api_key(&value)?;\n    Ok(value)\n}\n\nfn read_key_hidden(provider: &str) -> Result<String> {\n    if !io::stdin().is_terminal() {\n        bail!(\"interactive key entry requires a terminal; use `--api-key-stdin` for piped input\");\n    }\n    let term = console::Term::stderr();\n    term.write_str(&format!(\"Enter {provider} API key: \"))\n        .context(\"failed to write API key prompt\")?;\n    let value = term\n        .read_secure_line()\n        .context(\"failed to read API key securely\")?;\n    term.write_line(\"\").ok();\n    let value = value.trim().to_string();\n    validate_api_key(&value)?;\n    Ok(value)\n}\n\nfn json_body(value: &impl Serialize) -> Result<Vec<u8>> {\n    serde_json::to_vec(value).context(\"failed to encode Codewhale account request\")\n}\n\nfn expect_json<T: DeserializeOwned>(response: CloudResponse, statuses: &[u16]) -> Result<T> {","sourceCodeStart":956,"sourceCodeEnd":992,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/cli/src/cloud.rs#L956-L992","documentation":"Hidden (masked) API-key entry requires an interactive terminal on stdin, because it renders a secure no-echo prompt via console::Term. This bail fires when the key-add/login path falls back to the hidden prompt while stdin is not a TTY - typically in pipes, scripts, and CI - and tells you to use the explicit stdin mode instead.","triggerScenarios":"Running a command that prompts for the key (no --api-key-stdin) with stdin redirected: `codewhale cloud login ... < file`, inside `curl ... | sh` style pipelines, in CI runners, or under ssh -T where no TTY is allocated.","commonSituations":"CI/CD jobs provisioning credentials; Docker containers without -t; scripts meant to be interactive being run non-interactively; automation reusing an interactive command.","solutions":["Use the explicit pipe mode: printf '%s' \"$KEY\" | codewhale ... --api-key-stdin","Or set the provider's env var (e.g. DEEPSEEK_API_KEY) instead of interactive entry","For real interactive use, allocate a TTY: run without redirection, or ssh -t / docker run -it","Rewrite automation to pass the key via config or secret store rather than a prompt"],"exampleFix":"# before\ncodewhale cloud login --provider deepseek        # stdin not a TTY -> error\n# after\nprintf '%s' \"$DEEPSEEK_API_KEY\" | codewhale cloud login --provider deepseek --api-key-stdin","handlingStrategy":"fallback","validationCode":"use std::io::IsTerminal;\n\nif !std::io::stdin().is_terminal() {\n    // fall back to explicit pipe mode instead of the hidden prompt\n    let key = read_key_from_stdin()?; // --api-key-stdin path\n} else {\n    let key = read_key_hidden(\"deepseek\")?;\n}","typeGuard":null,"tryCatchPattern":"match read_key_hidden(provider) {\n    Ok(k) => k,\n    Err(e) if e.to_string().contains(\"requires a terminal\") => read_key_from_stdin()?,\n    Err(e) => return Err(e),\n}","preventionTips":["Branch automation on std::io::stdin().is_terminal() and choose stdin mode up front","Always pass --api-key-stdin in CI/docker (no -t) contexts","Prefer env vars or the secret store for non-interactive provisioning"],"tags":["cloud","api-key","tty","stdin","automation"],"backgroundTag":"stdin-not-a-tty","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}