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

  1. Provide a non-empty `openai_api_key` in the update request
  2. Check the stored credential (an existing key satisfies the check — re-submit it if the field is blank by UI design)
  3. Switch `openai.key_option` to a non-BringYourOwn credential source (e.g. environment-based key)
  4. 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

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


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)