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

  1. Verify the secret source is non-empty (`test -n "$KEY"` before piping)
  2. Re-run with the actual key: `printf '%s' "$KEY" | codewhale auth set --provider <p> --api-key-stdin`
  3. 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

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


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)