affaan-m/ECC · error

missing orchestration template variable(s): {}

Error message

missing orchestration template variable(s): {}

What it means

Runtime error from the template interpolation routine in ecc2/src/config/mod.rs. As each ${key} placeholder is rendered, any key missing from the `vars` map is appended to a `missing` list and rendered as empty. After rendering, if `missing` is non-empty, it is sorted, de-duplicated, and the method bails with "missing orchestration template variable(s): {}" joined by ', '. The message lists every required variable the caller did not supply.

Source

Thrown at ecc2/src/config/mod.rs:878

    let mut missing = Vec::new();
    let rendered = placeholder.replace_all(value, |captures: &regex::Captures<'_>| {
        let key = captures
            .get(1)
            .map(|capture| capture.as_str())
            .unwrap_or_default();
        match vars.get(key) {
            Some(value) => value.to_string(),
            None => {
                missing.push(key.to_string());
                String::new()
            }
        }
    });

    if !missing.is_empty() {
        missing.sort();
        missing.dedup();
        anyhow::bail!(
            "missing orchestration template variable(s): {}",
            missing.join(", ")
        );
    }

    Ok(rendered.into_owned())
}

impl BudgetAlertThresholds {
    pub fn sanitized(self) -> Self {
        let values = [self.advisory, self.warning, self.critical];
        let valid = values.into_iter().all(f64::is_finite)
            && self.advisory > 0.0
            && self.advisory < self.warning
            && self.warning < self.critical
            && self.critical < 1.0;

        if valid {

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Provide every variable named in the message, matching casing exactly.
  2. Diff the template's ${...} placeholders against the vars you are passing.
  3. If a variable is optional, give it a default in the template or omit the placeholder.
  4. Generate vars from a single source of truth (env, file) to avoid drift.

Example fix

# before
template = "deploy ${env} ${region}"
vars = { env = "prod" }   # region missing

# after
template = "deploy ${env} ${region}"
vars = { env = "prod", region = "us-east-1" }
Defensive patterns

Strategy: validation

Validate before calling

// Extract ${...} placeholders and confirm vars supplies all of them.
use regex::Regex;
fn missing_vars(template: &str, vars: &BTreeMap<String, String>) -> Vec<String> {
    let re = Regex::new(r"\$\{([A-Za-z0-9_]+)\}").unwrap();
    let mut required: HashSet<String> = re.captures_iter(template)
        .map(|c| c[1].to_string()).collect();
    for k in vars.keys() { required.remove(k); }
    required.into_iter().collect()
}

Type guard

fn has_all_vars(template: &str, vars: &BTreeMap<String, String>) -> bool {
    missing_vars(template, vars).is_empty()
}

Try / catch

let resolved = cfg.resolve_orchestration_template(name, &vars).map_err(|e| {
    if e.to_string().contains("missing orchestration template variable") {
        anyhow::anyhow!("{e}; pass each as --var KEY=VALUE")
    } else {
        e
    }
})?;

Prevention

When it happens

Trigger: Calling resolve_orchestration_template (or its interpolation helper) when the template body or step fields reference ${VAR} placeholders that are absent from the supplied vars BTreeMap. The template is found and has steps, but the variable contract is unmet.

Common situations: A required --var was not passed on the CLI; the variable name in the template does not match the supplied key (typo, casing); the env-var source for a variable was not set so it never entered vars; a template was extended with new placeholders but the caller was not updated.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/1ac3ddbcc1f840b0. Report an issue: GitHub.