Ansible v2.19.12 - Async Timeout And Test Filtering


Ansible published v2.19.12 on 10 August 2026 as a stable patch on the 2.19 line of ansible-core. The change that shows up first on a controller that gathers facts in parallel is the async wrapper: it now consults the module timeout before it kills the process that is still running a fact module. This is not a prerelease.

The full release notes and downloads are on the GitHub release page.

Parallel fact gathering wraps each fact module in an async job. Before this tag, the wrapper slept 5 seconds, slept 5 seconds again, then asked whether the job still had time left. That pair of sleeps sat in front of the kill decision even when the configured timeout had already expired, or when the timeout was shorter than 10 seconds.

Operators who cap fact collection on large inventories feel this first. A short timeout on the setup module, or a play level timeout, did not map cleanly onto process lifetime. The wrapper could leave a fact module running past the budget. It could also delay the kill by a fixed 10 seconds of sleep. Bootstrap playbooks that inventory a worker fleet and then gather OS and package facts are the usual place this shows up as a stalled controller rather than a clean timeout.

v2.19.12 folds remaining time into the kill decision. The two 5 second sleeps are no longer the gate. Fact jobs that run long should die closer to the configured timeout instead of after an extra pair of sleeps.

This is a control loop fix, not a new fact cache and not a new gather subset. If fact gathering already finishes well inside the timeout, the play looks the same. If fact gathering was bumping the timeout on slow or wedged hosts, expect earlier kills and more timeout failures instead of a quiet stall.

The same parallel fact path could hang when async job files were corrupt. Partial writes, leftover files from a killed controller, and shared temp dirs on NFS are the usual sources. A hang here is worse than a timeout. The play never returns. The fork sits on the job file. CI or the orchestrator has to send SIGKILL to the controller.

The patch stops that hang. Corrupt job state should fail the async status read rather than block the gather. For a data platform that runs Ansible as node bootstrap before batch workers come up, that is the difference between a failed job with a log and a stuck runner that holds the slot.

This is a read path fix for a bad on disk job record, not a change to how facts are serialized. After a crash, check /tmp or the configured remote tmp on targets. Stale async files from older ansible-core runs are still worth deleting before the first gather on a reused image.

The remaining change is in ansible-test. Target filtering used to drop versions that the user passed when those versions were missing from the completion configuration. A CI job that pins an OS image not listed in the default completion data would lose the pin. The run then failed with an unknown image, or resolved a different target than the one in the pipeline.

That matters for collection and module test jobs that still need an older distro, a custom remote, or a version that ansible-test has not added to its completion list. The filter now preserves the versions the user specified.

This is a CI contract fix, not a new remote provider. If the pipeline already uses only names that exist in completion data, behavior stays the same. If the job passes an explicit version that is valid on the backend but absent from completion, that pin should survive filtering after v2.19.12.

v2.19.12 does not add features, does not change templating, and does not reopen the 2.19 porting work. Shops still on 2.18 should not treat this tag as the 2.19 migration. There is no new breaking change and no new config key. The timeout path now reads the timeout that was already set.

The GitHub tag page is a short stub. The itemized notes live in the 2.19 changelog on the v2.19.12 tree. Wheel and sdist hashes are on the GitHub release page for anyone who pins ansible-core from PyPI and wants to verify the bits:

  • Built distribution ansible_core-2.19.12-py3-none-any.whl, 2416747 bytes, SHA256 b6bbdf05952852ad908861d652a9009c8474f1fe9c63a77ec202979a329d7d99
  • Source distribution ansible_core-2.19.12.tar.gz, 3436037 bytes, SHA256 cfb63b021558d5daddb1d0cd35886a5e1b18db2fc254d1f342e1527c62dac058