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
- Check the exact rendered value in the message and compare with `ansible-galaxy collection list --format json` output on the worker.
- Upgrade/downgrade ansible-core to a version whose JSON output has a per-path object mapping collection names to objects.
- Ensure the job directory is passed as an absolute path so the expected `<job_dir>/ansible_collections` key matches.
- 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
- Pin ansible-core so `collection list --format json` output shape is stable
- Verify JSON output manually after any ansible upgrade
- Use absolute job_dir paths so the expected key matches
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
- Failed to parse inventory arg: {}
- Failed to read result: {}
- Completed jobs file must contain an array of jobs
- Failed to push completed jobs: ${e}
- Queued jobs file must contain an array of jobs
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/6c7cf1d72947ddf0.
Report an issue: GitHub.