windmill-labs/windmill · error

Expected an object (map) for the `ansible-galaxy collection

Error message

Expected an object (map) for the `ansible-galaxy collection list` command output and got {}

What it means

After `ansible-galaxy collection list` succeeds, its stdout is parsed as JSON and Windmill looks up the `<job_dir>/ansible_collections` key. If that key exists but is not a JSON object (map), this error is thrown because the per-collection data cannot be iterated as name→info entries.

Source

Thrown at backend/windmill-worker/src/ansible_executor.rs:1097

    let mut ret = HashMap::new();
    let mut logs = String::new();

    if !output.status.success() {
        let stderr = String::from_utf8(output.stderr)?;
        return Err(anyhow!(
            "Error getting ansible collection versions: {stderr}"
        ));
    }

    let stdout = String::from_utf8(output.stdout)?;

    let val: serde_json::Value = serde_json::from_str(&stdout)?;

    let Some(own_collections) = val.get(format!("{}/ansible_collections", job_dir)) else {
        return Ok((ret, logs));
    };

    let collections = own_collections.as_object().ok_or(anyhow!(
        "Expected an object (map) for the `ansible-galaxy collection list` command output and got {}",
        own_collections
    ))?;

    for (c_name, c) in collections.iter() {
        if let Some(v) = c.get("version").and_then(|v| v.as_str()) {
            // TODO: Check if version is not something like `(undefined)`
            ret.insert(c_name.clone(), v.to_string());
        } else {
            logs.push_str(&format!("Failed to get version for collection `{}`. Expected an object with a string in the `version` field but received {}\n", c_name, c));
        }
    }

    Ok((ret, logs))
}

pub async fn get_role_locks(job_dir: &str) -> anyhow::Result<(HashMap<String, String>, String)> {
    let mut ansible_cmd = Command::new(ANSIBLE_GALAXY_PATH.as_str());

View on GitHub (pinned to e474e8803c)

Solutions

  1. Check the exact rendered value in the message and compare with `ansible-galaxy collection list --format json` output on the worker.
  2. Upgrade/downgrade ansible-core to a version whose JSON output has a per-path object mapping collection names to objects.
  3. Ensure the job directory is passed as an absolute path so the expected `<job_dir>/ansible_collections` key matches.
  4. Report/patch the parser if a new ansible-core release changed the output contract.
Defensive patterns

Strategy: type-guard

Validate before calling

// sanity-check the shape the worker expects:
out=$(ansible-galaxy collection list --format json)
echo "$out" | jq -e --arg p "$JOB_DIR/ansible_collections" '.[$p] | type == "object"' || echo 'unexpected shape for expected key'

Type guard

fn is_collection_map(v: &serde_json::Value) -> bool { v.as_object().map(|m| m.values().all(|c| c.is_object())).unwrap_or(false) }

Try / catch

match serde_json::from_str::<serde_json::Value>(&stdout) {
    Ok(v) => /* proceed, but verify val[key].as_object() before iterating */,
    Err(e) => log::error!("ansible-galaxy JSON unparseable: {e}"),
}

Prevention

When it happens

Trigger: ansible-galaxy emits a `--format json` output whose `<job_dir>/ansible_collections` entry is a non-object value (array, string, null) — e.g. an unexpected CLI output format from a different ansible-core version, or the path key colliding with something odd in the environment.

Common situations: ansible-core version whose `collection list --format json` shape differs from what the worker expects; custom COLLECTIONS_PATHS producing odd output; piping/truncating stdout so the JSON is reshaped.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/6c7cf1d72947ddf0. Report an issue: GitHub.