Set_fact and delay Values Not Updating on Task Retries in Ansible Playbook

Hello everyone,

I’m encountering an issue with my Ansible playbook where the values set by set_fact and the delay values don’t seem to change during task retries. I suspect it might be related to using Jinja2 template expressions. Here’s the relevant part of my playbook.

  • name: Unit test
    hosts: localhost
    gather_facts: no

    tasks:

    • name: Mock test
      set_fact:
      status: “{{ [‘on’, ‘off’] | random }}”
      register: status
      until: status.ansible_facts.status != “off”
      retries: 5
      delay: >
      {% if ansible_retry_counter | default(0) == 0 %}
      {{ 2 + (1 | random(start=0)) }}
      {% else %}
      {{ (2 ** (ansible_retry_counter + 1)) + (1 | random(start=0)) }}
      {% endif %}

    • name: Print timestamp after all retries
      debug:
      msg: “Timestamp after checker creation: {{ lookup(‘pipe’, ‘date "+%Y-%m-%d %H:%M:%S"’) }}”

    • name: Debug
      debug:
      msg: “status: {{ status }}”

I suspect that variables using template expressions have fixed values.

$ansible --version
ansible 2.10.8
config file …

Any insights or suggestions would be greatly appreciated!

If that’s not possible, I think I need to handle it with a custom module…

TASK [Mock test] *******************************************************************************************************task path: /home/hamster/test.yml:6
FAILED - RETRYING: Mock test (5 retries left).Result was: {
“ansible_facts”: {
“status”: “off”
},
“attempts”: 1,
“changed”: false,
“retries”: 6
}
FAILED - RETRYING: Mock test (4 retries left).Result was: {
“ansible_facts”: {
“status”: “off”
},
“attempts”: 2,
“changed”: false,
“retries”: 6
}
FAILED - RETRYING: Mock test (3 retries left).Result was: {
“ansible_facts”: {
“status”: “off”
},
“attempts”: 3,
“changed”: false,
“retries”: 6
}
FAILED - RETRYING: Mock test (2 retries left).Result was: {
“ansible_facts”: {
“status”: “off”
},
“attempts”: 4,
“changed”: false,
“retries”: 6
}
FAILED - RETRYING: Mock test (1 retries left).Result was: {
“ansible_facts”: {
“status”: “off”
},
“attempts”: 5,
“changed”: false,
“retries”: 6
}
fatal: [localhost]: FAILED! => {
“ansible_facts”: {
“status”: “off”
},
“attempts”: 5,
“changed”: false
}

PLAY RECAP *************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0

I find it unreasonable that the random value remains the same consecutively.

other random value

TASK [Mock test] ***********************************************************************************************************************************
task path: /home/hamster/test.yml:6
ok: [localhost] => {
“ansible_facts”: {
“status”: “on”
},
“attempts”: 1,
“changed”: false
}

TASK [Print timestamp after all retries] ***********************************************************************************************************
task path: /home/hamster/test.yml:19
ok: [localhost] => {
“msg”: “Timestamp after checker creation: 2024-12-17 18:12:02”
}

TASK [Debug] ***************************************************************************************************************************************
task path: /home/hamster/test.yml:23
ok: [localhost] => {
“msg”: “status: {‘changed’: False, ‘ansible_facts’: {‘status’: ‘on’}, ‘failed’: False, ‘attempts’: 1}”
}

You are using the same variable status in set_fact and register. One is overwriting the other.

This is also incorrect. set_facts is not setting a variable in ansible_facts dictionary (maybe missleading?) but instead sets variable in hostvars[inventory_hostname][<your_variable>].

Then why doesn’t the delay value change when retrying a task? I manually measured the time using a stopwatch.

I presume, but I’m not 100% sure, that delay is evaluated only once, not on every retry.

1 Like

I also think that the delay and set_fact values are fixed at evaluation time.

1 Like

yes set_fact was a bad name, it is as you state, it is ‘set a host variable’.

Could the fixed values of set_fact and delay during evaluation be due to Jinja2 expressions? It seems like they don’t change even during retries.

If it’s confirmed that the values are fixed during evaluation, I would handle it with Python instead.

Yes, the module arg is templated once (see this explanation strings templated using a lookup in a loop shouldn't be cached and re-used · Issue #82955 · ansible/ansible · GitHub - there are also a few workarounds mentioned on that issue). If you’re able to update to 2.18, then you could do use loop for retries and loop_control.break_when to stop looping when the condition is met:

  - name: Set fact until status is "on"
    set_fact:
      status: "{{ ['on', 'off'] | random }}"
    register: status_result
    loop: "{{ range(0, 5) }}"
    loop_control:
      pause: >
        {% if (ansible_loop.index|default(0)) == 0 %}
        {{ 2 + (1 | random(start=0)) }}
        {% else %}
        {{ (2 ** (ansible_loop.index + 1)) + (1 | random(start=0)) }}
        {% endif %}
      break_when: status == "on"

Thank you, it seems I’ll need to handle it with Python in the current version.

1 Like

Another option is to move it somewhere that will be re-evaluated, for example strings templated using a lookup in a loop shouldn't be cached and re-used · Issue #82955 · ansible/ansible · GitHub (this will work on 2.10).

To clarify, delay is constant across the retry/until loop, it is only templated at the start of the loop, not every iteration.

1 Like

Template expressions wrapped in {{ }} appear to be evaluated only once.