Hmbown/CodeWhale · warning · anyhow::Error

key label must contain 1-{MAX_KEY_LABEL_CHARS} characters

Error message

key label must contain 1-{MAX_KEY_LABEL_CHARS} characters

What it means

Key labels (e.g. for `codewhale cloud key add --label`) are normalized by collapsing whitespace, then must be 1-80 characters with no control characters. This bail fires when the normalized label is empty, longer than 80 chars, or contains control bytes - for example a label made only of spaces collapses to empty and is rejected.

Source

Thrown at crates/cli/src/cloud.rs:895

fn validate_api_key(key: &str) -> Result<()> {
    let bytes = key.len();
    if bytes < MIN_API_KEY_BYTES || bytes as u64 > MAX_API_KEY_BYTES {
        bail!("API key must be {MIN_API_KEY_BYTES}-{MAX_API_KEY_BYTES} UTF-8 bytes");
    }
    if key.chars().any(is_ascii_control) {
        bail!("API key contains invalid control characters");
    }
    Ok(())
}

fn validate_label(label: &str) -> Result<String> {
    let label = label.split_whitespace().collect::<Vec<_>>().join(" ");
    if label.is_empty()
        || label.chars().count() > MAX_KEY_LABEL_CHARS
        || label.chars().any(is_ascii_control)
    {
        bail!("key label must contain 1-{MAX_KEY_LABEL_CHARS} characters");
    }
    Ok(label)
}

fn is_ascii_control(character: char) -> bool {
    character <= '\u{001f}' || character == '\u{007f}'
}

fn resolve_local_key(
    config: &ConfigStore,
    secrets: &Secrets,
    provider: CloudProvider,
) -> Result<Option<String>> {
    let kind = provider.local_kind();
    let provider_config = config.config.providers.for_provider(kind);
    let from_config = provider_config.api_key.clone().or_else(|| {
        (kind == ProviderKind::Deepseek)
            .then(|| config.config.api_key.clone())

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Provide a short non-empty label (default to something like the hostname or date if scripting: --label "${LABEL:-ci-$(date +%F)}")
  2. Trim the label to <=80 characters before passing it
  3. Remove newlines/tabs from scripted labels: --label "$(echo "$LABEL" | tr '\n' ' ')"
  4. Check for unset variables in scripts (set -u) so labels never silently become empty

Example fix

# before
codewhale cloud key add --label ""   # empty after collapse -> error
# after
codewhale cloud key add --label "${LABEL:-laptop-$(date +%F)}"
Defensive patterns

Strategy: validation

Validate before calling

const MAX_KEY_LABEL_CHARS: usize = 80;

fn label_ok(raw: &str) -> bool {
    let normalized = raw.split_whitespace().collect::<Vec<_>>().join(" ");
    !normalized.is_empty()
        && normalized.chars().count() <= MAX_KEY_LABEL_CHARS
        && !normalized.chars().any(|c| c <= '\u{001f}' || c == '\u{007f}')
}

Type guard

fn is_valid_label(label: &str) -> bool {
    let n = label.split_whitespace().collect::<Vec<_>>().join(" ");
    !n.is_empty() && n.chars().count() <= 80
        && !n.chars().any(|c| c <= '\u{001f}' || c == '\u{007f}')
}

Prevention

When it happens

Trigger: Passing --label ' ' (whitespace-only), a 100+ character description, or a label with embedded tabs/newlines; the value is whitespace-collapsed first so 'a\n\nb' passes but ' \t ' does not.

Common situations: Scripted key creation where the label variable is empty or unset (expands to blank); labels copied from ticket titles that exceed 80 chars; multi-line paste into a label prompt.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/4136641efd7cb20f. Report an issue: GitHub.