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` when `OPENAI_API_KEY` is set but not valid UTF-8. `OsString::into_string()` fails for non-UTF-8 byte sequences and the function maps that failure to this error, refusing to use a malformed credential.

Solutions

  1. Re-export with a clean UTF-8 value from the OpenAI console
  2. Inspect bytes (echo -n "$OPENAI_API_KEY" | xxd) and strip non-ASCII characters
  3. Fix the script/profile writing the variable to write UTF-8 text
  4. Store the key via GitButler settings to bypass the env var

Example fix

// before (invalid byte)
export OPENAI_API_KEY=$'sk-\xff...'

// after
export OPENAI_API_KEY=sk-proj-clean-key
Defensive patterns

Strategy: validation

Validate before calling

fn openai_key_is_utf8() -> bool {
    std::env::var_os("OPENAI_API_KEY")
        .map(|v| v.into_string().is_ok())
        .unwrap_or(false)
}

Try / catch

if let Err(e) = result {
    if e.to_string().contains("Invalid UTF-8 in OPENAI_API_KEY") {
        eprintln!("OPENAI_API_KEY has invalid bytes; re-copy as plain UTF-8");
    }
}

Prevention

When it happens

Trigger: `std::env::var_os("OPENAI_API_KEY")` succeeds but `.into_string()` errors because the value contains invalid UTF-8 bytes — corrupted export, wrong-encoding script, or binary paste into a dotfile.

Common situations: Pasting keys from editors/terminals that mangle encoding; locale misconfiguration (non-UTF-8 locale writing the var); provisioning scripts using binary tools without text mode.

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 gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/556753d225dc233c. Report an issue: GitHub.

Appendix: 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 58e5313667)