# Module\_defaults no longer accept values as templates

**URL:** <https://forum.ansible.com/t/module-defaults-no-longer-accept-values-as-templates/45026>\
**Category:** Get Help\
**Created:** [December 24, 2025, 4:29pm UTC](https://forum.ansible.com/t/module-defaults-no-longer-accept-values-as-templates/45026 "2025-12-24T16:29:29Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![noonedeadpunk](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/noonedeadpunk/32/7605_2.png) [@noonedeadpunk](https://forum.ansible.com/u/noonedeadpunk)\
**Post date:** [December 24, 2025, 4:29pm UTC](https://forum.ansible.com/t/module-defaults-no-longer-accept-values-as-templates/45026/1 "2025-12-24T16:29:29Z")

</div>

I am looking into our upgrade path to ansible-core 2.19 (which is /o\ so far), and stumbled across changed behavior which I haven’t found was documented in the upgrade guide ([Ansible-core 2.19 Porting Guide — Ansible Community Documentation](https://docs.ansible.com/projects/ansible/latest/porting_guides/porting_guide_core_2.19.html))

So we are having configurable `gather_subset` since ansible 2.4 or smth. Recently, in 2.18, we moved from ANSIBLE\_GATHER\_SUBSET to `module_defaults` as was suggested by the prior guide. In our playbooks we have smth like that today:

```auto
- name: Gather host facts
  hosts: "hosts"
  gather_facts: true
  vars:
    default_gather_subset:
      - '!all'
      - min
  module_defaults:
    ansible.builtin.setup:
      gather_subset: "{{ default_gather_subset }}"
  tags:
    - always

```

However, starting with ansible-core 2.19 this starts failing:

```auto
[ERROR]: Task failed: Action failed: The following modules failed to execute: ansible.legacy.setup.

Task failed: Action failed.

<<< caused by >>>

The following modules failed to execute: ansible.legacy.setup.

+--[Sub-Event 1 of 1]---
| 
| Bad subset '{{ default_gather_subset }}' given to Ansible. gather_subset options allowed: all, all_ipv4_addresses, all_ipv6_addresses, apparmor, architecture, caps, chroot, cmdline, date_time, default_ipv4, default_ipv6, devices, distribution, distribution_major_version, distribution_release, distribution_version, dns, effective_group_ids, effective_user_id, env, facter, fibre_channel_wwn, fips, hardware, interfaces, is_chroot, iscsi, kernel, kernel_version, loadavg, local, lsb, machine, machine_id, mounts, network, nvme, ohai, os_family, pkg_mgr, platform, processor, processor_cores, processor_count, python, python_version, real_user_id, selinux, service_mgr, ssh_host_key_dsa_public, ssh_host_key_ecdsa_public, ssh_host_key_ed25519_public, ssh_host_key_rsa_public, ssh_host_pub_keys, ssh_pub_keys, system, system_capabilities, system_capabilities_enforced, systemd, user, user_dir, user_gecos, user_gid, user_id, user_shell, user_uid, virtual, virtualization_role, virtualization_tech_guest, virtualization_tech_host, virtualization_type
| 
+--[End Sub-Event]---

fatal: [aio1]: FAILED! => 
    ansible_facts: {}
    changed: false
    failed_modules:
        ansible.legacy.setup:
            ansible_facts:
                discovered_interpreter_python: /usr/bin/python3.12
            exception: (traceback unavailable)
            failed: true
            invocation:
                module_args:
                    fact_path: /etc/ansible/facts.d
                    filter: []
                    gather_subset:
                    - '{{ default_gather_subset }}'
                    gather_timeout: 10
            msg: 'Bad subset ''{{ default_gather_subset }}'' given to Ansible. gather_subset
                options allowed: all, all_ipv4_addresses, all_ipv6_addresses, apparmor,
                architecture, caps, chroot, cmdline, date_time, default_ipv4, default_ipv6,
                devices, distribution, distribution_major_version, distribution_release,
                distribution_version, dns, effective_group_ids, effective_user_id, env,
                facter, fibre_channel_wwn, fips, hardware, interfaces, is_chroot, iscsi,
                kernel, kernel_version, loadavg, local, lsb, machine, machine_id, mounts,
                network, nvme, ohai, os_family, pkg_mgr, platform, processor, processor_cores,
                processor_count, python, python_version, real_user_id, selinux, service_mgr,
                ssh_host_key_dsa_public, ssh_host_key_ecdsa_public, ssh_host_key_ed25519_public,
                ssh_host_key_rsa_public, ssh_host_pub_keys, ssh_pub_keys, system, system_capabilities,
                system_capabilities_enforced, systemd, user, user_dir, user_gecos, user_gid,
                user_id, user_shell, user_uid, virtual, virtualization_role, virtualization_tech_guest,
                virtualization_tech_host, virtualization_type'
    msg: 'The following modules failed to execute: ansible.legacy.setup.'

```

Apparently, the root cause is that templates are no longer accepted as values. However, according to [ansible/changelogs/fragments/74039\_enable\_module\_defaults\_for\_collections.yml at 3b861abce13c541c1fc41e59d631e76d2564582a · ansible/ansible · GitHub](https://github.com/ansible/ansible/blob/3b861abce13c541c1fc41e59d631e76d2564582a/changelogs/fragments/74039_enable_module_defaults_for_collections.yml#L4) this supposed to work,

So am I missing smth?  
Is that expected behavior or a bug? As tests for the feature [ansible/test/integration/targets/gathering\_facts/test\_module\_defaults.yml at 5e10a9160c96b238d788b1b196a4c3e80ba6a8bd · ansible/ansible · GitHub](https://github.com/ansible/ansible/blob/5e10a9160c96b238d788b1b196a4c3e80ba6a8bd/test/integration/targets/gathering_facts/test_module_defaults.yml) does not seem to ever cover this.

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [December 24, 2025, 5:57pm UTC](https://forum.ansible.com/t/module-defaults-no-longer-accept-values-as-templates/45026/2 "2025-12-24T17:57:34Z")

</div>

I would assume this is a bug.

Replacing

```yaml
  gather_facts: true

```

by

```yaml
  gather_facts: false
  pre_tasks:
    - ansible.builtin.setup:

```

fixes the issue for me. I would expect that `gather_facts: true` basically adds `ansible.builtin.setup` as a pre-task (the code in lib/ansible/executor/play\_iterator.py looks like it’s basically doing that).

---

<div class="post-metadata">

**Author:** ![noonedeadpunk](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/noonedeadpunk/32/7605_2.png) [@noonedeadpunk](https://forum.ansible.com/u/noonedeadpunk)\
**Post date:** [December 24, 2025, 6:24pm UTC](https://forum.ansible.com/t/module-defaults-no-longer-accept-values-as-templates/45026/3 "2025-12-24T18:24:15Z")

</div>

So, implicit setup runs “conditionally” - it won’t be launched if cached facts are fresh, so it will quickly pass.

Which I believe is not true for explicit setup in pre\_tasks, unless there is some possibility to write a condition for that (not aware how though).

I can report a bug if current behavior is not intentional, was not sure if I am missing smth or it’s intended (with amount of things in 2.19 it’s always a possibility)

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [December 26, 2025, 8:57am UTC](https://forum.ansible.com/t/module-defaults-no-longer-accept-values-as-templates/45026/4 "2025-12-26T08:57:02Z")

</div>

I guess you have to use `ansible.builtin.gather_facts` instead of `ansible.builtin.setup` if you want to conditionally gather facts if they are no longer fresh. (That’s the actual action that lib/ansible/executor/play\_iterator.py runs unconditionally as the first task if `gather_facts` is `true`.)

Since

```yaml
  gather_facts: false
  pre_tasks:
    - ansible.builtin.gather_facts:

```

also fixes the issue for me, my guess is that this is a bug. I’d definitely create an issue to see what the Core team thinks about this…

---

<div class="post-metadata">

**Author:** ![noonedeadpunk](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/noonedeadpunk/32/7605_2.png) [@noonedeadpunk](https://forum.ansible.com/u/noonedeadpunk)\
**Post date:** [January 7, 2026, 3:33pm UTC](https://forum.ansible.com/t/module-defaults-no-longer-accept-values-as-templates/45026/5 "2026-01-07T15:33:54Z")

</div>

I have submitted a report:

> <https://github.com/ansible/ansible/issues/86383>
>
> \### Summary
> 
> There is a regression in ansible-core 2.19, where templated values …are no longer resolved for module\_defaults statement.
> 
> This works perfectly fine in 2.18, but not in 2.19.
> 
> Templated module\_defaults were never tested https://github.com/ansible/ansible/blob/5e10a9160c96b238d788b1b196a4c3e80ba6a8bd/test/integration/targets/gathering\_facts/test\_module\_defaults.yml but their support was documented:
> https://github.com/ansible/ansible/blob/3b861abce13c541c1fc41e59d631e76d2564582a/changelogs/fragments/74039\_enable\_module\_defaults\_for\_collections.yml#L4
> 
> \### Issue Type
> 
> Bug Report
> 
> \### Component Name
> 
> module\_defaults
> 
> \### Ansible Version
> 
> \`\`\`console
> $ ansible --version
> ansible \[core 2.19.5\]
> config file = None
> configured module search path = \['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'\]
> ansible python module location = /opt/ansible-runtime/lib/python3.12/site-packages/ansible
> ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
> executable location = /opt/ansible-runtime/bin/ansible
> python version = 3.12.3 (main, Nov 6 2025, 13:44:16) \[GCC 13.3.0\] (/opt/ansible-runtime/bin/python3)
> jinja version = 3.1.6
> pyyaml version = 6.0.3 (with libyaml v0.2.5)
> \`\`\`
> 
> \### Configuration
> 
> \`\`\`console
> \# if using a version older than ansible-core 2.12 you should omit the '-t all'
> $ ansible-config dump --only-changed
> CONFIG\_FILE() = None
> 
> GALAXY\_SERVERS:
> \`\`\`
> 
> \### OS / Environment
> 
> Ubuntu 24.04
> 
> \### Steps to Reproduce
> 
> \`\`\`yaml
> \- name: Gather host facts
> hosts: "hosts"
> gather\_facts: true
> vars:
> default\_gather\_subset:
> - '!all'
> - min
> module\_defaults:
> ansible.builtin.setup:
> gather\_subset: "{{ default\_gather\_subset }}"
> tags:
> - always
> \`\`\`
> 
> 
> \### Expected Results
> 
> Facts are gathered properly on playbook launched and limited to \`min\`.
> 
> \### Actual Results
> 
> \`\`\`console
> \[ERROR\]: Task failed: Action failed: The following modules failed to execute: ansible.legacy.setup.
> 
> Task failed: Action failed.
> 
> \<\<\< caused by \>\>\>
> 
> The following modules failed to execute: ansible.legacy.setup.
> 
> +--\[Sub-Event 1 of 1 \]---
> | 
> | Bad subset '{{ default\_gather\_subset }}' given to Ansible. gather\_subset options allowed: all, all\_ipv4\_addresses, all\_ipv6\_addresses, apparmor, architecture, caps, chroot, cmdline, date\_time, default\_ipv4, default\_ipv6, devices, distribution, distribution\_major\_version, distribution\_release, distribution\_version, dns, effective\_group\_ids, effective\_user\_id, env, facter, fibre\_channel\_wwn, fips, hardware, interfaces, is\_chroot, iscsi, kernel, kernel\_version, loadavg, local, lsb, machine, machine\_id, mounts, network, nvme, ohai, os\_family, pkg\_mgr, platform, processor, processor\_cores, processor\_count, python, python\_version, real\_user\_id, selinux, service\_mgr, ssh\_host\_key\_dsa\_public, ssh\_host\_key\_ecdsa\_public, ssh\_host\_key\_ed25519\_public, ssh\_host\_key\_rsa\_public, ssh\_host\_pub\_keys, ssh\_pub\_keys, system, system\_capabilities, system\_capabilities\_enforced, systemd, user, user\_dir, user\_gecos, user\_gid, user\_id, user\_shell, user\_uid, virtual, virtualization\_role, virtualization\_tech\_guest, virtualization\_tech\_host, virtualization\_type
> | 
> +--\[End Sub-Event \]---
> 
> fatal: \[aio1\]: FAILED! =\> 
> ansible\_facts: {}
> changed: false
> failed\_modules:
> ansible.legacy.setup:
> ansible\_facts:
> discovered\_interpreter\_python: /usr/bin/python3.12
> exception: (traceback unavailable)
> failed: true
> invocation:
> module\_args:
> fact\_path: /etc/ansible/facts.d
> filter: \[\]
> gather\_subset:
> - '{{ default\_gather\_subset }}'
> gather\_timeout: 10
> msg: 'Bad subset ''{{ default\_gather\_subset }}'' given to Ansible. gather\_subset
> options allowed: all, all\_ipv4\_addresses, all\_ipv6\_addresses, apparmor,
> architecture, caps, chroot, cmdline, date\_time, default\_ipv4, default\_ipv6,
> devices, distribution, distribution\_major\_version, distribution\_release,
> distribution\_version, dns, effective\_group\_ids, effective\_user\_id, env,
> facter, fibre\_channel\_wwn, fips, hardware, interfaces, is\_chroot, iscsi,
> kernel, kernel\_version, loadavg, local, lsb, machine, machine\_id, mounts,
> network, nvme, ohai, os\_family, pkg\_mgr, platform, processor, processor\_cores,
> processor\_count, python, python\_version, real\_user\_id, selinux, service\_mgr,
> ssh\_host\_key\_dsa\_public, ssh\_host\_key\_ecdsa\_public, ssh\_host\_key\_ed25519\_public,
> ssh\_host\_key\_rsa\_public, ssh\_host\_pub\_keys, ssh\_pub\_keys, system, system\_capabilities,
> system\_capabilities\_enforced, systemd, user, user\_dir, user\_gecos, user\_gid,
> user\_id, user\_shell, user\_uid, virtual, virtualization\_role, virtualization\_tech\_guest,
> virtualization\_tech\_host, virtualization\_type'
> msg: 'The following modules failed to execute: ansible.legacy.setup.'
> \`\`\`
> 
> \### Code of Conduct
> 
> \- \[x\] I agree to follow the Ansible Code of Conduct
