{"record":{"id":"48723306d5a19f84","repo":"gitbutlerapp/gitbutler","slug":"invalid-utf-8-in-anthropic-api-key","errorCode":null,"errorMessage":"Invalid UTF-8 in ANTHROPIC_API_KEY","messagePattern":"Invalid UTF-8 in ANTHROPIC_API_KEY","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-llm/src/anthropic.rs","lineNumber":159,"sourceCode":"        Ok((CredentialsKind::GitButlerProxied, creds))\n    }\n\n    fn anthropic_own_key_creds() -> Result<(CredentialsKind, Sensitive<String>)> {\n        let creds = secret::retrieve(AI_ANTHROPIC_SECRET_HANDLE, secret::Namespace::Global)?\n            .ok_or(anyhow::anyhow!(\n                \"No Anthropic own key configured. Add this through the GitButler settings\"\n            ))?;\n        Ok((CredentialsKind::OwnAnthropicKey, creds))\n    }\n\n    fn anthropic_env_var_creds() -> Result<(CredentialsKind, Sensitive<String>)> {\n        let creds = Sensitive(\n            std::env::var_os(\"ANTHROPIC_API_KEY\")\n                .ok_or(anyhow::anyhow!(\n                    \"Environment variable ANTHROPIC_API_KEY is not set\"\n                ))?\n                .into_string()\n                .map_err(|_| anyhow::anyhow!(\"Invalid UTF-8 in ANTHROPIC_API_KEY\"))?,\n        );\n        Ok((CredentialsKind::EnvVarAnthropicKey, creds))\n    }\n}\n\nimpl LLMClient for AnthropicProvider {\n    fn model(&self) -> Option<String> {\n        self.model.clone()\n    }\n\n    fn tool_calling_loop_stream(\n        &self,\n        system_message: &str,\n        chat_messages: Vec<ChatMessage>,\n        tool_set: &mut impl Toolset,\n        model: &str,\n        on_token: impl Fn(&str) + Send + Sync + 'static,\n    ) -> Result<(String, Vec<ChatMessage>)> {","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-llm/src/anthropic.rs#L141-L177","documentation":"Thrown by anthropic_env_var_creds in crates/but-llm/src/anthropic.rs when ANTHROPIC_API_KEY is present but its bytes are not valid UTF-8 (var_os succeeded, into_string failed). Keys are transmitted as strings, so a non-UTF-8 value — typically a mangled shell encoding or a binary-pasted value — is rejected before use.","triggerScenarios":"ANTHROPIC_API_KEY containing raw non-UTF-8 bytes (e.g. a Latin-1 or truncated multi-byte sequence); the variable set from a script that wrote binary data; locale/encoding corruption in the environment.","commonSituations":"Keys pasted into terminals with mismatched encodings; values assembled from command substitution that captured stray bytes; minimal container images with broken locale settings mangling exported strings.","solutions":["Re-set the variable with a clean ASCII key: export ANTHROPIC_API_KEY=\"sk-ant-...\".","Check for stray characters: print | xxd | head to inspect the bytes actually stored.","Set the variable from a file with known UTF-8/ASCII content (e.g. export ANTHROPIC_API_KEY=\"$(cat keyfile)\")."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Validate the key is clean UTF-8 before use\nmatch std::env::var(\"ANTHROPIC_API_KEY\") {\n    Ok(k) if !k.is_empty() => { /* proceed */ },\n    Ok(_) => eprintln!(\"ANTHROPIC_API_KEY is empty\"),\n    Err(std::env::VarError::NotUnicode(_)) => eprintln!(\"ANTHROPIC_API_KEY is not valid UTF-8; re-export it\"),\n    Err(std::env::VarError::NotPresent) => eprintln!(\"ANTHROPIC_API_KEY not set\"),\n}","typeGuard":"fn anthropic_env_key_is_valid() -> bool {\n    matches!(std::env::var(\"ANTHROPIC_API_KEY\"), Ok(k) if !k.is_empty() && k.is_ascii())\n}","tryCatchPattern":null,"preventionTips":["Store keys in single-quoted exports to avoid shell interpolation or encoding surprises.","In CI, inject secrets via the runner's secret mechanism rather than echoed shell lines.","Treat non-ASCII API keys as configuration bugs — provider keys are ASCII by construction."],"tags":["llm","anthropic","environment-variables","utf-8","encoding"],"backgroundTag":"invalid-env-var-encoding","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}