# Different behavior if code is ran with task or as separate task

**URL:** <https://forum.ansible.com/t/different-behavior-if-code-is-ran-with-task-or-as-separate-task/41226>\
**Category:** Get Help\
**Tags:** include\_tasks\
**Created:** [March 23, 2025, 5:36pm UTC](https://forum.ansible.com/t/different-behavior-if-code-is-ran-with-task-or-as-separate-task/41226 "2025-03-23T17:36:55Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Vinvar](https://avatars.discourse-cdn.com/v4/letter/v/ba8739/32.png) [@Vinvar](https://forum.ansible.com/u/Vinvar)\
**Post date:** [March 23, 2025, 5:36pm UTC](https://forum.ansible.com/t/different-behavior-if-code-is-ran-with-task-or-as-separate-task/41226/1 "2025-03-23T17:36:55Z")

</div>

Hello everyone, would someone be able to explain the behavior I am encountering (or tell me where I mess up 😉 )?

I have a dictionary which contains variables with (i.a.) filenames. The purpose of this is to copy files (plural!) from the local host to the remote host. As this is a recurring task in my repository I have created an utility script to verify & copy the files and whenever I need this kind of action in a playbook, I use ‘include\_tasks’ to call the utility script. The first step in the utility script is to check whether the filename is ‘yml’ or ‘j2’ as I later on need to use ‘ansible.builtin.copy’ or ‘…template’.

When I call this script I get an error that the task includes an option with an undefined variable. However, when I run the code from the utility script in the main script, it works. Upon troubleshooting I noticed that within the main script the loop is ran as a separate dictionary while in the utility script the loop is ran as one dictionary.

I have the following two stand-alone scripts to reproduce the issue.  
main.yml

```auto
- name: 'A script to show my problem.'
  hosts: 'localhost'
  check_mode: true
  gather_facts: true

  tasks:
    - name: 'Deduct the file format (within this scipt).'
      ansible.builtin.debug:
        msg: "{{ deduct_filename.value.from | split('.') | last }}"
      loop: "{{ lookup('dict', files) }}"
      loop_control:
        loop_var: 'deduct_filename'

    - name: 'Deduct the file format (with an helper script).'
      ansible.builtin.include_tasks: 'copy_file.yml'
      loop:
        - "{{ lookup('dict', files) }}"
      loop_control:
        loop_var: 'deduct_filename'

  vars:
    files:
      test_file_1:
        from: '/path/to/location.yml'
        to: 'not/important.yml'
      test_file_2:
        from: '/path/to/location.j2'
        to: 'not/important.j2'

```

copy\_file.yml

```auto
- name: 'Deduct the file format (within this scipt).'
  ansible.builtin.debug:
    msg: "{{ deduct_filename.value.from | split('.') | last }}"

  # in the actual script
  # task: when needed, validate file.
  # task: verify directory exists on remote host. If not, stop.
  # tasks: copy/template file with correct owner, group, mode.

```

Running main.yml results in the following output:

```auto
PLAY [A script to show all the dictonary (filter) options.] *****

TASK [Gathering Facts] *****
ok: [localhost]

TASK [Deduct the file format (within this script).] *****
ok: [localhost] => (item={'key': 'test_file_1', 'value': {'from': '/path/to/location.yml', 'to': 'not/important.yml'}}) => {
    "msg": "yml"
}
ok: [localhost] => (item={'key': 'test_file_2', 'value': {'from': '/path/to/location.j2', 'to': 'not/important.j2'}}) => {
    "msg": "j2"
}

TASK [Deduct the file format (with an helper script).] *****
included: copy_file.yml for localhost => (item=[{'key': 'test_file_1', 'value': {'from': '/path/to/location.yml', 'to': 'not/important.yml'}}, {'key': 'test_file_2', 'value': {'from': '/path/to/location.j2', 'to': 'not/important.j2'}}])

TASK [Show passed along variable.] *****
ok: [localhost] => {
    "msg": [
        {
            "key": "test_file_1",
            "value": {
                "from": "/path/to/location.yml",
                "to": "not/important.yml"
            }
        },
        {
            "key": "test_file_2",
            "value": {
                "from": "/path/to/location.j2",
                "to": "not/important.j2"
            }
        }
    ]
}

TASK [Reduct the file format (within this scipt).] *****
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: Unable to look up a name or access an attribute in template string ({{ deduct_filename.value.from | split('.') | last }}).\nMake sure your variable name does not contain invalid characters like '-': descriptor 'split' for 'str' objects doesn't apply to a 'AnsibleUndefined' object. descriptor 'split' for 'str' objects doesn't apply to a 'AnsibleUndefined' object. Unable to look up a name or access an attribute in template string ({{ deduct_filename.value.from | split('.') | last }}).\nMake sure your variable name does not contain invalid characters like '-': descriptor 'split' for 'str' objects doesn't apply to a 'AnsibleUndefined' object. descriptor 'split' for 'str' objects doesn't apply to a 'AnsibleUndefined' object\n\nThe error appears to be in 'files_copy.yml': line 24, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: 'Reduct the file format (within this scipt).'\n ^ here\n"}

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

```

As you can see the task ‘Show passed along variable.’ returns the dictionary as one item where the task ‘Deduct the file format (within this script).’ above it returns it as two dictionaries.

Details about my Ansible version:

```auto
ansible [core 2.16.3]
  config file = [redacted]
  configured module search path = ['[redacted].ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = [redacted].ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.12.3 (main, Feb 4 2025, 14:48:35) [GCC 13.3.0] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True

```

---

<div class="post-metadata">

**Author:** ![anon81884396](https://avatars.discourse-cdn.com/v4/letter/a/b782af/32.png) [@anon81884396](https://forum.ansible.com/u/anon81884396)\
**Post date:** [March 23, 2025, 6:30pm UTC](https://forum.ansible.com/t/different-behavior-if-code-is-ran-with-task-or-as-separate-task/41226/2 "2025-03-23T18:30:56Z")

</div>

> [@Vinvar](#):
>
> ```auto
> tasks:
> - name: 'Deduct the file format (within this scipt).'
> ansible.builtin.debug:
> msg: "{{ deduct_filename.value.from | split('.') | last }}"
> loop: "{{ lookup('dict', files) }}"
> loop_control:
> loop_var: 'deduct_filename'
> 
> - name: 'Deduct the file format (with an helper script).'
> ansible.builtin.include_tasks: 'copy_file.yml'
> loop:
> - "{{ lookup('dict', files) }}"
> loop_control:
> loop_var: 'deduct_filename'
> 
> ```

The loop in your ‘Deduct the file format (with an helper script).’ has a dash in front of `"{{ lookup('dict', files) }}"`, making it a list comprised of a single dict. When I copied your script and corrected it like this, it ran correctly:

```auto
- name: 'A script to show my problem.'
  hosts: 'localhost'
  check_mode: true
  gather_facts: true

  tasks:
    - name: 'Deduct the file format (within this scipt).'
      ansible.builtin.debug:
        msg: "{{ deduct_filename.value.from | split('.') | last }}"
      loop: "{{ lookup('dict', files) }}"
      loop_control:
        loop_var: 'deduct_filename'

    - name: 'Deduct the file format (with an helper script).'
      ansible.builtin.include_tasks: 'copy_file.yml'
      loop: "{{ lookup('dict', files) }}"
      loop_control:
        loop_var: 'deduct_filename'

  vars:
    files:
      test_file_1:
        from: '/path/to/location.yml'
        to: 'not/important.yml'
      test_file_2:
        from: '/path/to/location.j2'
        to: 'not/important.j2'

```

---

<div class="post-metadata">

**Author:** ![Vinvar](https://avatars.discourse-cdn.com/v4/letter/v/ba8739/32.png) [@Vinvar](https://forum.ansible.com/u/Vinvar)\
**Post date:** [March 23, 2025, 6:53pm UTC](https://forum.ansible.com/t/different-behavior-if-code-is-ran-with-task-or-as-separate-task/41226/3 "2025-03-23T18:53:50Z")

</div>

Thanks! 😃

I was not aware these dashes ‘converts’ the variable to a list.

---

<div class="post-metadata">

**Author:** ![bcoca](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bcoca/32/9269_2.png) [@bcoca](https://forum.ansible.com/u/bcoca)\
**Post date:** [March 25, 2025, 2:52pm UTC](https://forum.ansible.com/t/different-behavior-if-code-is-ran-with-task-or-as-separate-task/41226/4 "2025-03-25T14:52:03Z")

</div>

> [@Vinvar](#):
>
> ` - "{{ lookup('dict', files) }}"`

This is YAML indicator of ‘list item’, it is not as much a conversion as a definition of it as a list.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/ansible/original/1X/1882d9a01da5d8996a862359b0913cc568a39b84.svg) [@system](https://forum.ansible.com/u/system)\
**Post date:** [April 24, 2025, 2:52pm UTC](https://forum.ansible.com/t/different-behavior-if-code-is-ran-with-task-or-as-separate-task/41226/5 "2025-04-24T14:52:08Z")

</div>

Closed on April 24, 2025, 2:52pm UTC

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
