{"record":{"id":"a397ce8cb57f1255","repo":"gitbutlerapp/gitbutler","slug":"enter-an-anthropic-api-key-ai","errorCode":null,"errorMessage":"Enter an Anthropic API key","messagePattern":"Enter an Anthropic API key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/ai.rs","lineNumber":188,"sourceCode":"    update: &AiConfigurationUpdate,\n    openai_has_key: bool,\n    anthropic_has_key: bool,\n) -> Result<()> {\n    let configuration = domain_configuration(update, DomainConfiguration::default())?;\n\n    if configuration.provider == LLMProviderKind::OpenAi\n        && configuration.openai.key_option == CredentialsKeyOption::BringYourOwn\n        && submitted_key(update.openai_api_key.clone()).is_none()\n        && !openai_has_key\n    {\n        bail!(\"Enter an OpenAI API key\")\n    }\n    if configuration.provider == LLMProviderKind::Anthropic\n        && configuration.anthropic.key_option == CredentialsKeyOption::BringYourOwn\n        && submitted_key(update.anthropic_api_key.clone()).is_none()\n        && !anthropic_has_key\n    {\n        bail!(\"Enter an Anthropic API key\")\n    }\n    Ok(())\n}\n\nfn domain_configuration(\n    update: &AiConfigurationUpdate,\n    mut configuration: DomainConfiguration,\n) -> Result<DomainConfiguration> {\n    configuration.provider = provider(&update.provider)?;\n    configuration.openai = OpenAiConfiguration {\n        key_option: key_option(\"OpenAI\", &update.openai_key_option)?,\n        model: update.openai_model.clone(),\n        custom_endpoint: update.openai_custom_endpoint.clone(),\n    };\n    configuration.anthropic = AnthropicConfiguration {\n        key_option: key_option(\"Anthropic\", &update.anthropic_key_option)?,\n        model: update.anthropic_model.clone(),\n    };","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-api/src/ai.rs#L170-L206","documentation":"Validation error from `validate_update` (called by `update_ai_configuration`): an Anthropic provider configuration using the BringYourOwn credential option requires an API key, but none was submitted and none exists in storage. Prevents saving an Anthropic configuration that could not authenticate.","triggerScenarios":"Calling the AI configuration update with provider=anthropic, anthropic.key_option=BringYourOwn, empty/absent `anthropic_api_key` in the update payload, and no previously stored Anthropic key.","commonSituations":"Switching provider to Anthropic without entering a key; clearing the key field and saving; wiping the credential store then attempting to update settings.","solutions":["Provide a non-empty `anthropic_api_key` in the update request","Keep the stored key (an existing key satisfies the check) instead of clearing it","Switch `anthropic.key_option` to a non-BringYourOwn credential source","Ensure the submitted key is not blank/whitespace"],"exampleFix":"// before\nawait api.updateAiConfiguration({ provider: \"Anthropic\", anthropic: { keyOption: \"BringYourOwn\" } });\n// after\nawait api.updateAiConfiguration({\n  provider: \"Anthropic\",\n  anthropic: { keyOption: \"BringYourOwn\" },\n  anthropicApiKey: process.env.ANTHROPIC_API_KEY,\n});","handlingStrategy":"validation","validationCode":"if (cfg.provider === \"anthropic\" && cfg.anthropic.keyOption === \"BringYourOwn\" &&\n    !update.anthropicApiKey?.trim() && !(await api.hasStoredAnthropicKey())) {\n  throw new Error(\"anthropic api key required before saving\");\n}","typeGuard":"function hasAnthropicKey(u: AiConfigUpdate): boolean {\n  return typeof u.anthropicApiKey === \"string\" && u.anthropicApiKey.trim().length > 0;\n}","tryCatchPattern":"try {\n  await api.updateAiConfiguration(update);\n} catch (e) {\n  if (String(e).includes(\"Enter an Anthropic API key\")) {\n    setFieldError(\"anthropicApiKey\", \"required for BringYourOwn credential mode\");\n  } else { throw e; }\n}","preventionTips":["Require the Anthropic key field when BringYourOwn is selected and no key is stored","Gate the save button on key presence in the settings UI","Keep the stored key unless the user explicitly provides a replacement","Trim inputs; whitespace-only keys are treated as absent"],"tags":["validation","api-key","anthropic","configuration"],"backgroundTag":"missing-api-key","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}