gitbutlerapp/gitbutler · error
Enter an OpenAI API key
Error message
Enter an OpenAI API key
What it means
Validation error from `validate_update` (called by `update_ai_configuration`): an OpenAI provider configuration using the BringYourOwn credential option requires an API key, but none was submitted and none exists in storage. The update is rejected so the provider is never left without usable credentials.
Solutions
- Provide a non-empty `openai_api_key` in the update request
- Check the stored credential (an existing key satisfies the check — re-submit it if the field is blank by UI design)
- Switch `openai.key_option` to a non-BringYourOwn credential source (e.g. environment-based key)
- Confirm the key is not only whitespace; `submitted_key` rejects blank values
Example fix
// before
const update = { openai: { keyOption: "BringYourOwn" } };
await api.updateAiConfiguration(update);
// after
const update = { openai: { keyOption: "BringYourOwn" }, openaiApiKey: key.trim() || undefined };
if (!update.openaiApiKey && !(await api.hasStoredOpenAiKey())) {
throw new Error("OpenAI API key required");
}
await api.updateAiConfiguration(update); Defensive patterns
Strategy: validation
Validate before calling
if (cfg.provider === "openai" && cfg.openai.keyOption === "BringYourOwn" &&
!update.openaiApiKey?.trim() && !(await api.hasStoredOpenAiKey())) {
throw new Error("openai api key required before saving");
} Type guard
function hasOpenAiKey(u: AiConfigUpdate): boolean {
return typeof u.openaiApiKey === "string" && u.openaiApiKey.trim().length > 0;
} Try / catch
try {
await api.updateAiConfiguration(update);
} catch (e) {
if (String(e).includes("Enter an OpenAI API key")) {
setFieldError("openaiApiKey", "required for BringYourOwn credential mode");
} else { throw e; }
} Prevention
- Make the API key field required in the form when keyOption=BringYourOwn and no key is stored
- Validate on the client before submitting the update
- Distinguish "clear key" intent from "save without key" intent in the UI
- Trim submitted keys; blank strings do not count
When it happens
Trigger: Calling the AI configuration update with provider=openai, openai.key_option=BringYourOwn, an empty/absent `openai_api_key` in the update payload, and no previously stored OpenAI key.
Common situations: Switching the key option to BringYourOwn without pasting a key; clearing the key field in the settings UI and saving; first-time setup where the key vault is empty.
Understand the failure class
Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.
Related errors
- Enter an Anthropic API key
- Enter an OpenAI API key
- Environment variable OPENAI_API_KEY is not set
- No OpenAI own key configured. Add this through the…
- When using OpenAI in a bring your own key configuration…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/714f50f7f91f1fd2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-api/src/ai.rs:181
value.and_then(|value| {
let value = value.trim();
(!value.is_empty()).then(|| value.to_string())
})
}
fn validate_update(
update: &AiConfigurationUpdate,
openai_has_key: bool,
anthropic_has_key: bool,
) -> Result<()> {
let configuration = domain_configuration(update, DomainConfiguration::default())?;
if configuration.provider == LLMProviderKind::OpenAi
&& configuration.openai.key_option == CredentialsKeyOption::BringYourOwn
&& submitted_key(update.openai_api_key.clone()).is_none()
&& !openai_has_key
{
bail!("Enter an OpenAI API key")
}
if configuration.provider == LLMProviderKind::Anthropic
&& configuration.anthropic.key_option == CredentialsKeyOption::BringYourOwn
&& submitted_key(update.anthropic_api_key.clone()).is_none()
&& !anthropic_has_key
{
bail!("Enter an Anthropic API key")
}
Ok(())
}
fn domain_configuration(
update: &AiConfigurationUpdate,
mut configuration: DomainConfiguration,
) -> Result<DomainConfiguration> {
configuration.provider = provider(&update.provider)?;
configuration.openai = OpenAiConfiguration {
key_option: key_option("OpenAI", &update.openai_key_option)?,View on GitHub (pinned to 58e5313667)