{"record":{"id":"decf6f3b07cb5930","repo":"Hmbown/CodeWhale","slug":"machine-key-env-is-not-a-well-formed-codewhale-api-key-so-it","errorCode":null,"errorMessage":"{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`.","messagePattern":"(.+?) is not a well-formed Codewhale API key, so it was not sent\\. Expected (.+?) 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`\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/cli/src/cloud/machine.rs","lineNumber":98,"sourceCode":"// ---------------------------------------------------------------------------\n\n/// A validated machine token.\n///\n/// No `Display`, and `Debug` prints only the non-secret head, so the value\n/// cannot reach a panic message or a `{:?}` dump by accident.\n#[derive(Clone)]\npub(crate) struct MachineKey(String);\n\nimpl MachineKey {\n    /// Validate a raw environment value without sending it anywhere.\n    ///\n    /// A malformed value is almost always a truncated or shell-mangled paste.\n    /// Saying so locally is strictly more useful than a server 401, which\n    /// cannot distinguish \"you pasted half a key\" from \"this key was deleted\".\n    pub(crate) fn parse(raw: &str) -> Result<Self> {\n        let value = unwrap_quoted(raw);\n        if !token_is_well_formed(value) {\n            bail!(\n                \"{MACHINE_KEY_ENV} is not a well-formed Codewhale API key, so it was not sent. \\\nExpected {TOKEN_LEN} characters shaped `cwc_key_<24 hex>_<43 chars>`; got {} characters. \\\nThat is almost always a truncated or shell-mangled paste — re-copy the value, or create a \\\nnew key with `codewhale account api-keys create`.\",\n                value.chars().count()\n            );\n        }\n        Ok(Self(value.to_string()))\n    }\n\n    /// The non-secret 32-character head: `cwc_key_` plus the 24-hex key id.\n    #[must_use]\n    pub(crate) fn head(&self) -> &str {\n        &self.0[..TOKEN_HEAD_LEN]\n    }\n\n    /// Hand the full token to the transport. The only caller is this module.\n    fn expose(&self) -> String {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/cli/src/cloud/machine.rs#L80-L116","documentation":"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.","triggerScenarios":"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).","commonSituations":"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_...`.","solutions":["Re-copy the full key from `codewhale account api-keys list` and re-export the env var","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}$`","Remove surrounding quotes if you pasted them from JSON/YAML: export the raw value","Create a new key with `codewhale account api-keys create` if the original cannot be recovered"],"exampleFix":"// before\nexport CODEWHALE_MACHINE_KEY=\"cwc_key_9f2a...\"   # truncated\n// after\nexport CODEWHALE_MACHINE_KEY=\"cwc_key_a1b2c3d4e5f60718293a4b5c_x7Yz...(43 chars)\"","handlingStrategy":"validation","validationCode":"// shell check before exporting\nif ! echo -n \"$MACHINE_KEY_ENV\" | grep -Eq '^cwc_key_[0-9a-f]{24}_[A-Za-z0-9]{43}$'; then\n  echo \"machine key malformed\"; exit 1\nfi","typeGuard":"fn is_well_formed_machine_key(raw: &str) -> bool {\n    let v = raw.trim().trim_matches('\"');\n    v.len() == 24 + 1 + 43 + \"cwc_key_\".len()\n        && v.starts_with(\"cwc_key_\")\n        && v[8..32].bytes().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())\n}","tryCatchPattern":null,"preventionTips":["Copy machine keys whole from a trusted source; never retype them","Validate the shape with a regex before exporting to the environment","Keep the key in a single-line secret file and load it with trimming","Rotate via `codewhale account api-keys create` rather than hand-editing old values"],"tags":["cli","api-key","env-var","validation"],"backgroundTag":"invalid-env-var-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}