Hmbown/CodeWhale · error
empty API key provided
Error message
empty API key provided
What it means
The `--api-key-stdin` reader trims the line read from stdin and rejects it when empty, refusing to store a blank API key. The surrounding read errors carry a distinct context, so this bail specifically means input arrived but contained no key.
Solutions
- Verify the secret source is non-empty (`test -n "$KEY"` before piping)
- Re-run with the actual key: `printf '%s' "$KEY" | codewhale auth set --provider <p> --api-key-stdin`
- Use the hidden interactive prompt (`codewhale auth set --provider <p>`) if stdin plumbing is unreliable
Example fix
// before echo "$OPENAI_API_KEY" | codewhale auth set --provider openai --api-key-stdin # KEY unset // after [ -n "$OPENAI_API_KEY" ] && printf '%s' "$OPENAI_API_KEY" | codewhale auth set --provider openai --api-key-stdin
Defensive patterns
Strategy: validation
Validate before calling
if [ -z "${KEY//[[:space:]]/}" ]; then echo "key is empty"; exit 1; fi
printf '%s' "$KEY" | codewhale auth set --provider "$PROVIDER" --api-key-stdin Prevention
- Assert secrets are non-empty before piping (`test -n "$KEY"`)
- Check CI secret bindings actually inject the variable
- Avoid echo; use printf '%s' to avoid stray newlines
When it happens
Trigger: Piping an empty string, whitespace-only input, or an unset-variable expansion to `codewhale auth set --provider <p> --api-key-stdin`.
Common situations: `echo "$KEY"` where KEY is unset/empty; secrets manager returning empty in CI; piping a file whose relevant line is blank; copy-paste of a placeholder.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- No API key provided via stdin.
- API key contains invalid control characters
- API key id must be lowercase hex characters — the part…
- API key must be - UTF-8 bytes
- API key name must be 1
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/0b2287b7487146c3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/cli/src/lib.rs:4381
None => "n/a",
}
}
fn prompt_api_key(slot: &str) -> Result<String> {
use std::io::{IsTerminal, Write};
eprint!("Enter API key for {slot}: ");
io::stderr().flush().ok();
if !io::stdin().is_terminal() {
// Non-interactive: read directly without prompting twice.
return read_api_key_from_stdin();
}
let mut buf = String::new();
io::stdin()
.read_line(&mut buf)
.context("failed to read API key from stdin")?;
let key = buf.trim().to_string();
if key.is_empty() {
bail!("empty API key provided");
}
Ok(key)
}
/// Move plaintext keys from config.toml into the configured secret store.
/// Hidden in v0.8.8 because the normal setup path is config/env only.
fn run_auth_migrate(store: &mut ConfigStore, secrets: &Secrets, dry_run: bool) -> Result<()> {
let mut migrated: Vec<(ProviderKind, &'static str)> = Vec::new();
let mut warnings: Vec<String> = Vec::new();
let literal =
|value: &String| classify_config_api_key_value(value) == ConfigApiKeyValueKind::Literal;
for provider in ProviderKind::ALL {
let slot = provider_slot(provider);
let from_provider_block = store
.config
.providers
.for_provider(provider)View on GitHub (pinned to 73e0f67d83)