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
- Export the variable in the environment that actually runs goose: export MY_GATEWAY_HOST=... (check case and trailing whitespace)
- Add a safe fallback in the JSON: {"name": "MY_GATEWAY_HOST", "default": "https://fallback.example.com", "required": false}
- If the placeholder is optional, drop it from env_vars or set required: false so missing values just skip substitution
- 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
- Add a pre-flight env check (fail-fast, listing every missing var) before loading declarative providers
- Give required vars a 'default' when a safe fallback exists so missing env degrades instead of aborting
- Inject secrets via the service manager/environment files (systemd EnvironmentFile, k8s secrets) rather than manual exports
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
- AZURE_FOUNDRY_MODEL is required for MaaS endpoints
- Invalid provider type: {}
- Failed to parse {}: {}
- Provider '{}' not found. Available: {}
- Empty editor command
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/e5ec0e62bc509ae7.
Report an issue: GitHub.