gitbutlerapp/gitbutler · error
Environment variable ANTHROPIC_API_KEY is not set
Error message
Environment variable ANTHROPIC_API_KEY is not set
What it means
This error is thrown by `anthropic_env_var_creds` in the Anthropic LLM provider when no `ANTHROPIC_API_KEY` environment variable is present in the process environment. The function falls back to reading the key from the environment when no stored (own-key) credentials are available, so a missing variable means no usable API credential at all. It is an anyhow error raised before any network request is made.
Solutions
- Set the environment variable before running: export ANTHROPIC_API_KEY=sk-ant-...
- If launched from a GUI (desktop app), set the key via launchctl setenv / system environment settings so the child process inherits it
- Configure a stored Anthropic key through GitButler settings so the env-var fallback is not needed
- In CI, add ANTHROPIC_API_KEY as a secret environment input to the job
Example fix
// before $ but ai ... Error: Environment variable ANTHROPIC_API_KEY is not set // after $ export ANTHROPIC_API_KEY=sk-ant-api03-... $ but ai ...
Defensive patterns
Strategy: validation
Validate before calling
fn have_anthropic_key() -> bool {
std::env::var_os("ANTHROPIC_API_KEY")
.map(|v| !v.is_empty())
.unwrap_or(false)
}
if !have_anthropic_key() { eprintln!("Set ANTHROPIC_API_KEY before calling the AI provider"); } Try / catch
match provider.new() {
Err(e) if e.to_string().contains("ANTHROPIC_API_KEY") => {
eprintln!("Set ANTHROPIC_API_KEY in your environment");
}
Err(e) => return Err(e),
Ok(p) => p,
} Prevention
- Add ANTHROPIC_API_KEY to your shell profile or direnv .envrc
- For GUI apps, set the key at the session/system level, not just interactive shells
- Inject the secret explicitly in CI/CD job configuration
- Prefer storing keys in GitButler settings over env-var fallback
When it happens
Trigger: Calling the Anthropic provider's credential resolution path (`anthropic_env_var_creds`) when `std::env::var_os("ANTHROPIC_API_KEY")` returns None — i.e. the variable is unset or set to an empty-but-absent value is not possible (empty string still counts as set), so strictly: the variable is not defined in the environment.
Common situations: Running `but` CLI or the desktop app's Rust backend without exporting ANTHROPIC_API_KEY; CI environments where secrets are not injected; having the key set only in a shell profile not loaded by the GUI-launched process; typos in the variable name.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- Environment variable OPENAI_API_KEY is not set
- No Anthropic own key configured. Add this through the…
- Enter an Anthropic API key
- Enter an OpenAI API key
- Invalid UTF-8 in ANTHROPIC_API_KEY
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/3776862c21550077.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-llm/src/anthropic.rs:155
let creds = secret::retrieve(GITBUTLER_ACCESS_TOKEN_HANDLE, secret::Namespace::BuildKind)?
.ok_or(anyhow::anyhow!(
"No GitButler token available. Log-in to use the GitButler Anthropic provider"
))?;
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>,View on GitHub (pinned to 58e5313667)