zeroclaw-labs/zeroclaw · error

cloud_ops.iac_tools must not be empty when cloud_ops is enab

Error message

cloud_ops.iac_tools must not be empty when cloud_ops is enabled

What it means

ZeroClaw rejects a config in which the [cloud_ops] section is enabled but declares no infrastructure-as-code tools. The check runs at the end of CloudOpsConfig::validate() (after default_cloud, supported_clouds, and cost-threshold guards) because an enabled cloud_ops deployment is expected to manage at least one IaC tool such as terraform or pulumi. Fresh configs inherit a non-empty default list (default_cloud_ops_iac_tools()), so hitting this almost always means an explicit `iac_tools = []` or a config layer that cleared the list.

Source

Thrown at crates/zeroclaw-config/src/schema.rs:18600

                        "cloud_ops.supported_clouds[{i}] must not be empty"
                    );
                }
            }
            if !self.supported_clouds.contains(&self.default_cloud) {
                anyhow::bail!(
                    "cloud_ops.default_cloud '{}' is not in cloud_ops.supported_clouds {:?}",
                    self.default_cloud,
                    self.supported_clouds
                );
            }
            if self.cost_threshold_monthly_usd < 0.0 {
                anyhow::bail!(
                    "cloud_ops.cost_threshold_monthly_usd must be non-negative, got {}",
                    self.cost_threshold_monthly_usd
                );
            }
            if self.iac_tools.is_empty() {
                anyhow::bail!("cloud_ops.iac_tools must not be empty when cloud_ops is enabled");
            }
        }
        Ok(())
    }
}

fn default_cloud_ops_cloud() -> String {
    "aws".into()
}

fn default_cloud_ops_supported_clouds() -> Vec<String> {
    vec!["aws".into(), "azure".into(), "gcp".into()]
}

fn default_cloud_ops_iac_tools() -> Vec<String> {
    vec!["terraform".into()]
}

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Set at least one tool identifier: `iac_tools = ["terraform"]` under [cloud_ops]
  2. If you do not use IaC tooling, set `[cloud_ops] enabled = false` so the whole guard block is skipped
  3. Audit config layers (base file, profiles, overlays, ZEROCLAW_* env mirrors) for a layer that overrides iac_tools with an empty list after the base sets it
  4. While fixing, verify default_cloud, supported_clouds, and cost_threshold_monthly_usd too — those guards run first and often fail together once cloud_ops is enabled

Example fix

# before
[cloud_ops]
enabled = true
iac_tools = []

# after
[cloud_ops]
enabled = true
iac_tools = ["terraform", "pulumi"]
Defensive patterns

Strategy: validation

Validate before calling

fn cloud_ops_precheck(cfg: &zeroclaw_config::Config) -> Result<(), String> {
    let ops = &cfg.cloud_ops;
    if ops.enabled && ops.iac_tools.is_empty() {
        return Err("cloud_ops.iac_tools must not be empty when cloud_ops is enabled".into());
    }
    Ok(())
}

Type guard

fn has_iac_tools_when_enabled(cfg: &zeroclaw_config::Config) -> bool {
    !cfg.cloud_ops.enabled || !cfg.cloud_ops.iac_tools.is_empty()
}

Try / catch

match config.validate() {
    Err(err) if err.to_string().contains("cloud_ops.iac_tools") => {
        // populate iac_tools or disable [cloud_ops], then reload the config
    }
    other => other?,
}

Prevention

When it happens

Trigger: Set `[cloud_ops] enabled = true` together with `iac_tools = []`, or apply an overlay/profile/env-mirror layer that replaces the default list with an empty array. Config::validate() aborts with this message before the application starts.

Common situations: Trimming a copied example config down to the bone and emptying arrays instead of deleting keys; enabling cloud_ops purely for cost monitoring without wanting IaC tooling; overlay configs that concatenate/override lists and end up writing an empty list over the populated default.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/4971e8a37f460aa2. Report an issue: GitHub.