Hmbown/CodeWhale · error

is not a well-formed Codewhale API key, so it was not sent…

Error message

{MACHINE_KEY_ENV} is not a well-formed Codewhale API key, so it was not sent. Expected {TOKEN_LEN} characters shaped `cwc_key_<24 hex>_<43 chars>`; got {} characters. That is almost always a truncated or shell-mangled paste — re-copy the value, or create a new key with `codewhale account api-keys create`.

What it means

MachineKeyEnv::parse validates the environment-provided Codewhale API key against the exact token shape `cwc_key_<24 hex>_<43 chars>` (TOKEN_LEN characters). A value that fails token_is_well_formed is refused locally and never sent, with a message showing the actual character count, because a malformed value is almost always a truncated or shell-mangled paste and a local diagnosis beats an ambiguous server 401.

Solutions

  1. Re-copy the full key from `codewhale account api-keys list` and re-export the env var
  2. Verify length and shape: `echo -n "$MACHINE_KEY_ENV" | wc -c` should equal TOKEN_LEN, and it should match `^cwc_key_[0-9a-f]{24}_[A-Za-z0-9]{43}$`
  3. Remove surrounding quotes if you pasted them from JSON/YAML: export the raw value
  4. Create a new key with `codewhale account api-keys create` if the original cannot be recovered

Example fix

// before
export CODEWHALE_MACHINE_KEY="cwc_key_9f2a..."   # truncated
// after
export CODEWHALE_MACHINE_KEY="cwc_key_a1b2c3d4e5f60718293a4b5c_x7Yz...(43 chars)"
Defensive patterns

Strategy: validation

Validate before calling

// shell check before exporting
if ! echo -n "$MACHINE_KEY_ENV" | grep -Eq '^cwc_key_[0-9a-f]{24}_[A-Za-z0-9]{43}$'; then
  echo "machine key malformed"; exit 1
fi

Type guard

fn is_well_formed_machine_key(raw: &str) -> bool {
    let v = raw.trim().trim_matches('"');
    v.len() == 24 + 1 + 43 + "cwc_key_".len()
        && v.starts_with("cwc_key_")
        && v[8..32].bytes().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
}

Prevention

When it happens

Trigger: Setting MACHINE_KEY_ENV to a value that is not exactly TOKEN_LEN characters matching cwc_key_ + 24 hex + _ + 43 chars — truncated paste, quotes left in (unwrap_quoted strips one level), wrong variable exported, or a key from another provider (crates/cli/src/cloud/machine.rs:98, RAISED IN parse).

Common situations: Copy that stopped mid-key, a shell history entry with escaped/missing characters, wrapping quotes captured from JSON, editing the key in an editor that line-wrapped it, or exporting a placeholder like `cwc_key_...`.

Understand the failure class

Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.

Related errors


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

Appendix: source

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

// ---------------------------------------------------------------------------

/// A validated machine token.
///
/// No `Display`, and `Debug` prints only the non-secret head, so the value
/// cannot reach a panic message or a `{:?}` dump by accident.
#[derive(Clone)]
pub(crate) struct MachineKey(String);

impl MachineKey {
    /// Validate a raw environment value without sending it anywhere.
    ///
    /// A malformed value is almost always a truncated or shell-mangled paste.
    /// Saying so locally is strictly more useful than a server 401, which
    /// cannot distinguish "you pasted half a key" from "this key was deleted".
    pub(crate) fn parse(raw: &str) -> Result<Self> {
        let value = unwrap_quoted(raw);
        if !token_is_well_formed(value) {
            bail!(
                "{MACHINE_KEY_ENV} is not a well-formed Codewhale API key, so it was not sent. \
Expected {TOKEN_LEN} characters shaped `cwc_key_<24 hex>_<43 chars>`; got {} characters. \
That is almost always a truncated or shell-mangled paste — re-copy the value, or create a \
new key with `codewhale account api-keys create`.",
                value.chars().count()
            );
        }
        Ok(Self(value.to_string()))
    }

    /// The non-secret 32-character head: `cwc_key_` plus the 24-hex key id.
    #[must_use]
    pub(crate) fn head(&self) -> &str {
        &self.0[..TOKEN_HEAD_LEN]
    }

    /// Hand the full token to the transport. The only caller is this module.
    fn expose(&self) -> String {

View on GitHub (pinned to 73e0f67d83)