aaif-goose/goose · error
apiKey is required when auth is enabled and no secret is sto
Error message
apiKey is required when auth is enabled and no secret is stored
What it means
update_custom_provider reuses the existing api_key_env (or generates `<ID>_API_KEY` when empty). When requires_auth is true and the update params carry no api_key, it requires a secret to already exist under that name in Config; otherwise it errors. Conversely, when auth is disabled it deletes a previously generated key secret.
Source
Thrown at crates/goose/src/config/declarative_providers.rs:246
Ok(provider_config)
}
pub fn update_custom_provider(params: UpdateCustomProviderParams) -> Result<()> {
let loaded_provider = load_provider(¶ms.id)?;
let existing_config = loaded_provider.config;
let editable = loaded_provider.is_editable;
let config = Config::global();
let api_key_env = if params.requires_auth {
let api_key_name = if existing_config.api_key_env.is_empty() {
generate_api_key_name(¶ms.id)
} else {
existing_config.api_key_env.clone()
};
if let Some(api_key) = params.api_key.as_deref() {
config.set_secret(&api_key_name, &api_key)?;
} else if config.get_secret::<String>(&api_key_name).is_err() {
return Err(anyhow::anyhow!(
"apiKey is required when auth is enabled and no secret is stored"
));
}
api_key_name
} else {
if existing_config.api_key_env == generate_api_key_name(¶ms.id) {
config.delete_secret(&existing_config.api_key_env)?;
}
String::new()
};
if editable {
let model_infos: Vec<ModelInfo> = params
.models
.into_iter()
.map(|name| ModelInfo::new(name, 128000))
.collect();
View on GitHub (pinned to 3810898a74)
Solutions
- Pass api_key in the update params once — it is stored under api_key_env and future edits need no key
- Or store the secret manually under the existing api_key_env name (e.g. ACME_API_KEY) via the goose secret store
- If the provider genuinely needs no auth now, update with requires_auth: false (this also cleans up the generated secret)
Example fix
// before
let params = UpdateCustomProviderParams { requires_auth: true, api_key: None, .. };
update_custom_provider(params)?;
// after
let params = UpdateCustomProviderParams {
requires_auth: true,
api_key: Some(read_api_key_from_user()),
..
};
update_custom_provider(params)?; Defensive patterns
Strategy: validation
Validate before calling
use goose::config::Config;
// before update_custom_provider with requires_auth and no api_key:
if params.requires_auth && params.api_key.is_none() {
let key_name = if existing_config.api_key_env.is_empty() {
generate_api_key_name(¶ms.id)
} else {
existing_config.api_key_env.clone()
};
if Config::global().get_secret::<String>(&key_name).is_err() {
// prompt the user for the key and include it in the update params
}
} Try / catch
match update_custom_provider(params) {
Err(e) if e.to_string().contains("apiKey is required when auth is enabled") => {
// re-ask for the key, then retry the update with api_key: Some(...)
}
other => other?,
} Prevention
- Migrate the secret store together with custom provider JSONs to new machines
- Always offer re-entering the key when editing auth-required providers
- Note that disabling auth deletes the generated <ID>_API_KEY secret — re-enabling then requires the key again
When it happens
Trigger: Editing an existing auth-required provider without re-entering the key while the secret under its api_key_env is absent — e.g. the provider JSON was created by hand, the secret was deleted from goose config, or the key name drifted after an id change.
Common situations: Hand-authored provider JSON with requires_auth true but no matching secret ever stored; moving config to a new machine without migrating the secret store; secret removed via `goose configure`.
Related errors
- apiKey cannot be empty
- missing required key {}: {}
- Required environment variable {} is not set
- Invalid provider id: provider id cannot be empty
- Invalid provider id: {}
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/4c22e61320a2a499.
Report an issue: GitHub.