aaif-goose/goose · error

Required environment variable {} is not set

Error message

Required environment variable {} is not set

What it means

Declarative (custom) providers expand `${VAR_NAME}` placeholders in string fields via expand_env_vars. Each var's EnvVarConfig says whether it is secret and required. When a placeholder is present, the var is marked required, has no default, and is found neither via Config::get_secret (secret vars) nor Config::get_param, expansion aborts naming the missing variable.

Source

Thrown at crates/goose/src/config/declarative_providers.rs:46

pub fn expand_env_vars(template: &str, env_vars: &[EnvVarConfig]) -> Result<String> {
    let config = Config::global();
    let mut result = template.to_string();
    for var in env_vars {
        let placeholder = format!("${{{}}}", var.name);
        if !result.contains(&placeholder) {
            continue;
        }
        let value = if var.secret {
            config.get_secret::<String>(&var.name).ok()
        } else {
            config.get_param::<String>(&var.name).ok()
        };
        let value = match value {
            Some(v) => v,
            None => match &var.default {
                Some(d) => d.clone(),
                None if var.required => {
                    return Err(anyhow::anyhow!(
                        "Required environment variable {} is not set",
                        var.name
                    ));
                }
                None => continue,
            },
        };
        result = result.replace(&placeholder, &value);
    }
    Ok(result)
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoadedProvider {
    pub config: DeclarativeProviderConfig,
    pub is_editable: bool,
}

View on GitHub (pinned to 3810898a74)

Solutions

  1. Store the value in goose config: plain vars via set_param / `goose config set --params MY_VAR value`, secret vars via the secret store (`goose configure`) so get_secret succeeds
  2. Or add a `default` to the env_vars entry in the provider JSON
  3. Or set `"required": false` if the placeholder is optional
  4. Check the placeholder text matches the env_vars name exactly, including case

Example fix

// before (custom provider JSON)
"base_url": "${ACME_BASE_URL}",
"env_vars": [{ "name": "ACME_BASE_URL", "required": true }]

// after
"base_url": "${ACME_BASE_URL}",
"env_vars": [{ "name": "ACME_BASE_URL", "required": true, "default": "https://api.acme.com/v1" }]
Defensive patterns

Strategy: validation

Validate before calling

use goose::config::Config;

fn missing_required_placeholders(template: &str, env_vars: &[EnvVarConfig]) -> Vec<String> {
    let config = Config::global();
    env_vars
        .iter()
        .filter(|v| {
            template.contains(&format!("${{{}}}", v.name))
                && v.default.is_none()
                && if v.secret {
                    config.get_secret::<String>(&v.name).is_err()
                } else {
                    config.get_param::<String>(&v.name).is_err()
                }
        })
        .map(|v| v.name.clone())
        .collect()
}

let missing = missing_required_placeholders(&template, &env_vars);
if !missing.is_empty() { /* prompt for and store these vars before loading the provider */ }

Try / catch

match expand_env_vars(&template, &env_vars) {
    Err(e) if e.to_string().starts_with("Required environment variable") => {
        let var = e.to_string().rsplit(' ').next().unwrap_or_default();
        // collect `var` from the user, store it (set_secret/set_param), then retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: Loading or using a custom provider whose JSON in the custom_providers dir contains `${MY_VAR}` in a field such as base_url, while its env_vars entry declares {name: MY_VAR, required: true} with no default, and MY_VAR was never stored in goose config (params for plain vars, secret store for secret vars).

Common situations: Sharing a custom provider JSON between machines without transferring the stored values; renaming the var in the JSON but not the stored config entry; deleting the secret later; marking a var required by mistake; placeholder spelling drift between template and env_vars name.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/9fce26eb589fc672. Report an issue: GitHub.