gitbutlerapp/gitbutler · error

Invalid UTF-8 in OPENAI_API_KEY

Error message

Invalid UTF-8 in OPENAI_API_KEY

What it means

Thrown by openai_env_var_creds in crates/but-llm/src/openai.rs when OPENAI_API_KEY exists but into_string() fails because the value contains bytes that are not valid UTF-8. As with the Anthropic variant, the key must be a valid string before it can be used for API authentication, so a mangled value is rejected here.

Source

Thrown at crates/but-llm/src/openai.rs:106

    }

    fn openai_own_key_creds() -> Result<(CredentialsKind, Sensitive<String>)> {
        let creds = secret::retrieve(AI_OPENAI_SECRET_HANDLE, secret::Namespace::Global)?.ok_or(
            anyhow::anyhow!(
                "No OpenAI own key configured. Add this through the GitButler settings"
            ),
        )?;
        Ok((CredentialsKind::OwnOpenAiKey, creds))
    }

    fn openai_env_var_creds() -> Result<(CredentialsKind, Sensitive<String>)> {
        let creds = Sensitive(
            std::env::var_os("OPENAI_API_KEY")
                .ok_or(anyhow::anyhow!(
                    "Environment variable OPENAI_API_KEY is not set"
                ))?
                .into_string()
                .map_err(|_| anyhow::anyhow!("Invalid UTF-8 in OPENAI_API_KEY"))?,
        );
        Ok((CredentialsKind::EnvVarOpenAiKey, creds))
    }
}

impl OpenAIClientProvider for OpenAiProvider {
    fn client(&self) -> Result<Client<OpenAIConfig>> {
        match &self.credentials {
            (CredentialsKind::EnvVarOpenAiKey, _) => {
                let config = self.configure_custom_endpoint(OpenAIConfig::new());
                Ok(Client::with_config(config))
            }
            (CredentialsKind::OwnOpenAiKey, key) => {
                let config =
                    self.configure_custom_endpoint(OpenAIConfig::new().with_api_key(key.0.clone()));
                Ok(Client::with_config(config))
            }

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Re-export the variable with a clean ASCII key: export OPENAI_API_KEY="sk-...".
  2. Inspect the stored bytes: printenv OPENAI_API_KEY | xxd | head.
  3. Load the key from a UTF-8 file to avoid shell mangling.
Defensive patterns

Strategy: validation

Validate before calling

match std::env::var("OPENAI_API_KEY") {
    Ok(k) if !k.is_empty() => { /* proceed */ },
    Err(std::env::VarError::NotUnicode(_)) => eprintln!("OPENAI_API_KEY is not valid UTF-8; re-export it"),
    Err(_) => eprintln!("OPENAI_API_KEY missing or empty"),
}

Type guard

fn openai_env_key_is_valid() -> bool {
    matches!(std::env::var("OPENAI_API_KEY"), Ok(k) if !k.is_empty() && k.is_ascii())
}

Prevention

When it happens

Trigger: OPENAI_API_KEY set from binary or wrongly-encoded data (bad locale, truncated multi-byte sequence, stray control bytes); environment assembled by scripts that captured non-text output into the variable.

Common situations: Pasting keys through terminals with encoding mismatches; command substitution injecting extra bytes; container images with broken locale settings.

Understand the failure class

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/556753d225dc233c. Report an issue: GitHub.