Hmbown/CodeWhale · error
interactive key entry requires a terminal; use…
Error message
interactive key entry requires a terminal; use `--api-key-stdin` for piped input
What it means
read_key_hidden provides interactive masked key entry via the terminal. It requires stdin to be a TTY; when stdin is not a terminal (piped, redirected, or in CI) it bails with guidance to use --api-key-stdin instead, since the interactive prompt cannot read from a non-TTY stdin securely.
Solutions
- Use the non-interactive path: pipe the key with --api-key-stdin (e.g. `codewhale ... --api-key-stdin < key.txt`)
- Run the command in an interactive terminal
- For ssh, allocate a TTY: `ssh -t host codewhale login`
- In CI, store the key in a secret and pass it via --api-key-stdin
Example fix
// before read -r KEY; codewhale account api-keys import # stdin not a TTY // after printf '%s' "$KEY" | codewhale account api-keys import --api-key-stdin
Defensive patterns
Strategy: fallback
Validate before calling
// shell: detect non-TTY and switch input mode if [ -t 0 ]; then codewhale account api-keys import else printf '%s' "$KEY" | codewhale account api-keys import --api-key-stdin fi
Prevention
- In scripts and CI, always use --api-key-stdin or --api-key
- Use `ssh -t` when an interactive prompt is needed over ssh
- Check `[ -t 0 ]` before invoking interactive commands
When it happens
Trigger: Running a command that prompts for an API key interactively while stdin is not a TTY — e.g. `echo x | codewhale login ...`, running under a script or CI without `--api-key-stdin`, or inside an IDE/automation harness with redirected stdin (crates/cli/src/cloud.rs:1245).
Common situations: CI pipelines without TTY allocation, `ssh host codewhale login` without -t, scripts invoking the CLI expecting a prompt, or background jobs with stdin closed or redirected from /dev/null.
Related errors
- API key input is unexpectedly large
- API key on stdin exceeds the 8 KiB limit
- Codewhale TUI requires an interactive terminal (stdin and…
- external credential consent was not saved: non-interactive…
- Failed to enable raw mode
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/8fcdf553183fca25.
Report an issue: GitHub.
Appendix: source
Thrown at crates/cli/src/cloud.rs:1245
.take(MAX_API_KEY_STDIN_BYTES + 1)
.read_to_end(&mut bytes)
.context("failed to read API key from stdin")?;
parse_key_input(bytes)
}
fn parse_key_input(bytes: Vec<u8>) -> Result<String> {
if bytes.len() as u64 > MAX_API_KEY_STDIN_BYTES {
bail!("API key input is unexpectedly large");
}
let value = String::from_utf8(bytes).context("API key from stdin is not valid UTF-8")?;
let value = value.trim().to_string();
validate_api_key(&value)?;
Ok(value)
}
fn read_key_hidden(provider: &str) -> Result<String> {
if !io::stdin().is_terminal() {
bail!("interactive key entry requires a terminal; use `--api-key-stdin` for piped input");
}
let term = console::Term::stderr();
term.write_str(&format!("Enter {provider} API key: "))
.context("failed to write API key prompt")?;
let value = term
.read_secure_line()
.context("failed to read API key securely")?;
term.write_line("").ok();
let value = value.trim().to_string();
validate_api_key(&value)?;
Ok(value)
}
fn json_body(value: &impl Serialize) -> Result<Vec<u8>> {
serde_json::to_vec(value).context("failed to encode Codewhale account request")
}
fn expect_json<T: DeserializeOwned>(response: CloudResponse, statuses: &[u16]) -> Result<T> {View on GitHub (pinned to 73e0f67d83)