aaif-goose/goose · error

Required environment variable {} is not set

Error message

Required environment variable {} is not set

What it means

Declarative provider configs allow ${VAR} placeholders in base_url, base_path, and header values. resolve_placeholders walks the declared env_vars: when a template contains ${VAR}, std::env::var(VAR) fails, no 'default' is declared, and the entry is marked required: true, resolution aborts with this error naming the missing variable.

Source

Thrown at crates/goose-providers/src/declarative.rs:227

        std::env::var(key)
    }
}

fn expand_env_vars(template: &str, env_vars: &[EnvVarConfig]) -> Result<String> {
    let mut result = template.to_string();

    for var in env_vars {
        let placeholder = format!("${{{}}}", var.name);
        if !result.contains(&placeholder) {
            continue;
        }

        let value = match std::env::var(&var.name) {
            Ok(value) => value,
            Err(_) => match &var.default {
                Some(default) => default.clone(),
                None if var.required => {
                    anyhow::bail!("Required environment variable {} is not set", var.name)
                }
                None => continue,
            },
        };

        result = result.replace(&placeholder, &value);
    }

    Ok(result)
}

fn resolve_config(config: &mut DeclarativeProviderConfig) -> Result<()> {
    if let Some(env_vars) = &config.env_vars {
        config.base_url = expand_env_vars(&config.base_url, env_vars)?;

        for var in env_vars {
            if var.name.ends_with("_STREAMING") {
                let value = std::env::var(&var.name)

View on GitHub (pinned to 3810898a74)

Solutions

  1. Export the variable in the environment that actually runs goose: export MY_GATEWAY_HOST=... (check case and trailing whitespace)
  2. Add a safe fallback in the JSON: {"name": "MY_GATEWAY_HOST", "default": "https://fallback.example.com", "required": false}
  3. If the placeholder is optional, drop it from env_vars or set required: false so missing values just skip substitution
  4. Print the process env (env | grep -i my_gateway) in the same context goose runs in to confirm visibility

Example fix

// before (provider.json)
"base_url": "https://${MY_GATEWAY_HOST}/v1",
"env_vars": [{"name": "MY_GATEWAY_HOST", "required": true}]

// after
export MY_GATEWAY_HOST=api.internal.example.com   # in the launching shell/unit
// or in JSON:
"env_vars": [{"name": "MY_GATEWAY_HOST", "required": false, "default": "https://api.internal.example.com"}]
Defensive patterns

Strategy: validation

Validate before calling

fn required_env_vars_present(templates: &[&str], vars: &[EnvVar]) -> anyhow::Result<()> {
    let joined = templates.join(" ");
    for v in vars {
        if joined.contains(&format!("${{{}}}", v.name))
            && v.required
            && v.default.is_none()
            && std::env::var(&v.name).is_err() {
            anyhow::bail!("export {} before starting", v.name);
        }
    }
    Ok(())
}

Try / catch

// Pre-flight env check at startup with an actionable message listing ALL missing
// vars at once, instead of failing one-by-one during placeholder resolution:
let missing: Vec<_> = collect_missing_required_vars(&config);
anyhow::ensure!(missing.is_empty(), "missing env vars: {}", missing.join(", "));

Prevention

When it happens

Trigger: A custom provider JSON whose base_url is e.g. "https://${MY_GATEWAY_HOST}/v1" with {"name": "MY_GATEWAY_HOST", "required": true} in env_vars, run in a shell/process where MY_GATEWAY_HOST is not exported (or exported only in a different session, systemd unit, or container layer).

Common situations: Config authored on one machine, executed by a service/CI where the env var isn't provisioned; var name case mismatch (env vars are case-sensitive); shell exports inside a script that exits before goose runs; pod/container missing the secret injection.

Related errors


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