Hmbown/CodeWhale · error

API key id must be lowercase hex characters — the part…

Error message

API key id must be {KEY_ID_LEN} lowercase hex characters — the part after `cwc_key_` in the display prefix. Run `codewhale account api-keys list` to see them.

What it means

The key-id argument must be exactly KEY_ID_LEN lowercase hex characters — the segment immediately after `cwc_key_` in the key's display prefix. The validator trims the input, checks length and hex-ness (digits and a-f only), and bails with instructions to find ids via `codewhale account api-keys list`.

Solutions

  1. Run `codewhale account api-keys list` and copy the id exactly as shown
  2. Take only the substring after `cwc_key_` (the 24-hex segment), not the whole key
  3. Lowercase any uppercase hex characters
  4. Trim whitespace and strip surrounding quotes

Example fix

// before
codewhale account api-keys revoke cwc_key_A1B2C3D4E5F60718293A4B5C
// after
codewhale account api-keys revoke a1b2c3d4e5f60718293a4b5c
Defensive patterns

Strategy: validation

Validate before calling

fn key_id_ok(id: &str, len: usize) -> bool {
    let id = id.trim();
    id.len() == len && id.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
}

Prevention

When it happens

Trigger: Passing a key id to a revoke/delete command that is the wrong length, contains uppercase hex (A–F), includes the whole key/prefix, or contains non-hex characters (crates/cli/src/cloud/machine.rs:855).

Common situations: Passing the full key token instead of just the id after `cwc_key_`, copying the id with surrounding whitespace or quotes, uppercase hex from a formatted display, or confusing the key id with a key label.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/b265c3c17a00021a. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/cloud/machine.rs:855

            normalized.push(scope.to_string());
        }
    }
    Ok(Some(normalized))
}

/// The 24-hex key id, checked locally.
///
/// This is a paste check, not an existence check: the server answers 404
/// identically for a malformed id, an unknown id, and another account's id, so
/// nothing here can or should try to distinguish them.
pub(crate) fn validate_key_id(id: &str) -> Result<&str> {
    let id = id.trim();
    if id.len() != KEY_ID_LEN
        || !id
            .bytes()
            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
    {
        bail!(
            "API key id must be {KEY_ID_LEN} lowercase hex characters — the part after `cwc_key_` \
in the display prefix. Run `codewhale account api-keys list` to see them."
        );
    }
    Ok(id)
}

/// Refuse a management command that would be authenticated by a machine key.
///
/// The server would answer 403 `api_key_route_denied`, but a local refusal is
/// better: it never puts the credential on the wire, and it names the fix.
/// This is the load-bearing rule of the whole design — a stolen key must not
/// be able to bootstrap a successor that outlives the revocation of the key
/// that was stolen — so the CLI states it rather than discovering it.
pub(crate) fn reject_machine_key_for_management(
    machine: &MachineKeyEnv,
    has_session: bool,
) -> Result<()> {

View on GitHub (pinned to 73e0f67d83)