Fixing the Ansible Error: 'Invalid data passed to loop', it requires a list, got <class 'dict'>

beginner🔧 Ansible2026-07-24| Ansible 2.5+ running on any Linux distribution (Ubuntu, RHEL, Debian) or macOS.

Error Message

Invalid data passed to 'loop', it requires a list, got <class 'dict'>
#ansible#devops#troubleshooting#yaml

Understanding the Error

You’re running a playbook to deploy 15 Nginx virtual hosts, and everything seems fine until the task hits a wall. Ansible stops cold and throws this specific error:

Invalid data passed to 'loop', it requires a list, got <class 'dict'>

Ansible is being literal here. The loop keyword, which became the standard in version 2.5, strictly requires a YAML list (an array). If you pass it a dictionary—a set of key-value pairs wrapped in curly braces—the task will fail instantly. It’s like trying to put a square peg in a round hole.

Why This Happens

Most of the time, this snag happens because of how data is structured or captured. Here are the three most common culprits:

  • Direct Dictionary Usage: You defined a variable as a dictionary but tried to loop over it as if it were a simple list.
  • Registered Variables: You captured the output of a module like find, but you're trying to loop over the entire result object instead of the list of files inside it.
  • YAML Syntax Slips: A missing dash (-) in your variable file can accidentally turn a list into a dictionary.

Step-by-Step Fixes

Scenario 1: Converting a Dictionary on the Fly

Imagine you have a list of users defined in vars/main.yml like this:

# vars/main.yml
users:
  alice: 
    uid: 1001
    shell: /bin/bash
  bob:
    uid: 1002
    shell: /bin/zsh

If you try to use loop: "{{ users }}", Ansible sees a dictionary and fails. The fix is the dict2items filter. It reshapes your dictionary into a list that Ansible can digest.

# The Corrected Task
- name: Create users from a dictionary
  ansible.builtin.user:
    name: "{{ item.key }}"
    uid: "{{ item.value.uid }}"
    shell: "{{ item.value.shell }}"
  loop: "{{ users | dict2items }}"

Scenario 2: Navigating Registered Variables

This is a classic trap when using the find or stat modules. These modules return a massive dictionary containing metadata, timestamps, and status codes. The actual list you want is usually buried one level deeper.

# The WRONG way
- name: Find old log files
  ansible.builtin.find:
    paths: /var/log/nginx
    patterns: "*.log.gz"
  register: found_logs

- name: Cleanup logs
  ansible.builtin.file:
    path: "{{ item.path }}"
    state: absent
  loop: "{{ found_logs }}" # This fails!

The Fix: You must point the loop to the files attribute. That is where the actual list lives.

# The RIGHT way
- name: Cleanup logs
  ansible.builtin.file:
    path: "{{ item.path }}"
    state: absent
  loop: "{{ found_logs.files }}" # Target the specific list

Scenario 3: Spotting YAML Formatting Errors

YAML is sensitive. If you forget a dash, your data structure changes entirely. Take a look at this comparison:

# Incorrect: This is a Dictionary
my_packages:
  git: present
  vim: present

# Correct: This is a List
my_packages:
  - git
  - vim

Check your variable files. If your loop points to my_packages, ensure every item starts with a dash. Without those dashes, Ansible sees a dictionary and triggers the error.

Verification Steps

Unsure what data type you're dealing with? Use the debug module and the type_debug filter to inspect your variables mid-run. It saves a lot of guesswork.

- name: Check the data type
  ansible.builtin.debug:
    msg: "Variable type is {{ users | type_debug }}"

- name: Verify the conversion
  ansible.builtin.debug:
    msg: "After filter, type is {{ (users | dict2items) | type_debug }}"

If the output says list, you are good to go.

Practical Tips for Success

Visualizing YAML can be tough as playbooks grow. When I'm stuck, I use a YAML to JSON converter. JSON is much more rigid with its brackets and braces, making it obvious whether you’ve created an Object (Dictionary) or an Array (List).

One final trick: run your playbook with -v. Ansible will print the full JSON structure of your registered variables. This lets you see exactly which key holds the list you need for your loop without having to guess the attribute name.

Related Error Notes