gitbutlerapp/gitbutler · error
Invalid UTF-8 in ANTHROPIC_API_KEY
Error message
Invalid UTF-8 in ANTHROPIC_API_KEY
What it means
Thrown by `anthropic_env_var_creds` when `ANTHROPIC_API_KEY` exists in the environment but is not valid UTF-8. The function uses `OsString::into_string()`, which fails for non-UTF-8 bytes (e.g. on Unix, arbitrary byte sequences are legal in env vars), and maps that failure to this error. The provider refuses to proceed with a malformed credential rather than sending garbage to the API.
Solutions
- Re-export the variable with a clean ASCII/UTF-8 value: export ANTHROPIC_API_KEY=$(cat key.txt) after verifying key.txt is UTF-8
- Inspect the raw bytes (e.g. `echo -n "$ANTHROPIC_API_KEY" | xxd`) and remove non-ASCII characters
- Re-copy the key fresh from the Anthropic console and avoid rich-text paste
- Alternatively store the key via GitButler settings instead of the environment
Example fix
// before (shell, contains invalid byte) export ANTHROPIC_API_KEY=$'sk-ant-\xff...' // after export ANTHROPIC_API_KEY=sk-ant-api03-clean-utf8-key
Defensive patterns
Strategy: validation
Validate before calling
fn anthropic_key_is_utf8() -> bool {
std::env::var_os("ANTHROPIC_API_KEY")
.map(|v| v.into_string().is_ok())
.unwrap_or(false)
} Try / catch
if let Err(e) = creds_result {
if e.to_string().contains("Invalid UTF-8") {
eprintln!("ANTHROPIC_API_KEY contains non-UTF-8 bytes; re-copy the key");
}
} Prevention
- Copy keys as plain text from the provider console; avoid rich-text paste
- Ensure dotfiles and provisioning scripts are saved as UTF-8
- Verify with `echo -n "$ANTHROPIC_API_KEY" | xxd | grep -v ' 41\| 73'` style byte checks when in doubt
- Keep terminal/locale in UTF-8 (export LANG=C.UTF-8)
When it happens
Trigger: `std::env::var_os("ANTHROPIC_API_KEY")` succeeds but the returned OsString fails `into_string()` — the variable contains invalid UTF-8 bytes, typically from a corrupted shell profile, a bad export, or binary paste into a config file.
Common situations: Pasting a key with a broken multi-byte character from a rich-text editor; shell profile encoding issues; a script writing the key with wrong encoding; secrets managers emitting raw bytes.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid UTF-8 in OPENAI_API_KEY
- Environment variable ANTHROPIC_API_KEY is not set
- Environment variable OPENAI_API_KEY is not set
- No AI credentials found. Configure in GitButler settings or…
- Repository path is not valid UTF-8
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/48723306d5a19f84.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-llm/src/anthropic.rs:159
Ok((CredentialsKind::GitButlerProxied, creds))
}
fn anthropic_own_key_creds() -> Result<(CredentialsKind, Sensitive<String>)> {
let creds = secret::retrieve(AI_ANTHROPIC_SECRET_HANDLE, secret::Namespace::Global)?
.ok_or(anyhow::anyhow!(
"No Anthropic own key configured. Add this through the GitButler settings"
))?;
Ok((CredentialsKind::OwnAnthropicKey, creds))
}
fn anthropic_env_var_creds() -> Result<(CredentialsKind, Sensitive<String>)> {
let creds = Sensitive(
std::env::var_os("ANTHROPIC_API_KEY")
.ok_or(anyhow::anyhow!(
"Environment variable ANTHROPIC_API_KEY is not set"
))?
.into_string()
.map_err(|_| anyhow::anyhow!("Invalid UTF-8 in ANTHROPIC_API_KEY"))?,
);
Ok((CredentialsKind::EnvVarAnthropicKey, creds))
}
}
impl LLMClient for AnthropicProvider {
fn model(&self) -> Option<String> {
self.model.clone()
}
fn tool_calling_loop_stream(
&self,
system_message: &str,
chat_messages: Vec<ChatMessage>,
tool_set: &mut impl Toolset,
model: &str,
on_token: impl Fn(&str) + Send + Sync + 'static,
) -> Result<(String, Vec<ChatMessage>)> {View on GitHub (pinned to 58e5313667)