Introduction
The error list object has no attribute length is one of the most common Jinja2 mistakes in Ansible. It occurs when you try to access length as a Python attribute (.length) instead of using Jinja2's length filter (| length). This guide explains why, shows the fix, and covers all the essential Jinja2 list operations you should know.
The Error
# WRONG — causes "list object has no attribute length"
- name: Display list length
ansible.builtin.debug:
msg: "{{ my_list.length }}"
Error output:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable.
The error was: 'list object' has no attribute 'length'"}
The Fix
Use the | length Jinja2 filter:
# CORRECT
- name: Display list length
ansible.builtin.debug:
msg: "{{ my_list | length }}"
Why This Happens
Ansible uses Jinja2 for templating, not raw Python. In Jinja2:
- Attributes (dot notation) access object properties:
my_dict.key - Filters (pipe notation) transform values:
my_list | length
Python lists have a len() function, not a .length attribute. Jinja2 provides the length filter as the equivalent:
| Python | Jinja2 (Ansible) |
|---|---|
len(my_list) | {{ my_list | length }} |
my_list.sort() | {{ my_list | sort }} |
", ".join(my_list) | {{ my_list | join(", ") }} |
Practical Examples
Count Items in a List
vars:
packages:
- nginx
- postgresql
- redis
- memcached
tasks:
- name: Show package count
ansible.builtin.debug:
msg: "Installing {{ packages | length }} packages"
# Output: "Installing 4 packages"
Conditional Based on List Length
- name: Warn if too many hosts
ansible.builtin.debug:
msg: "WARNING: {{ ansible_play_hosts | length }} hosts in play — consider using serial"
when: ansible_play_hosts | length > 20
Check If List Is Empty
- name: Skip if no errors found
ansible.builtin.debug:
msg: "Processing {{ errors | length }} errors"
when: errors | length > 0
# Alternative: use truthiness
- name: Skip if no errors (simpler)
ansible.builtin.debug:
msg: "Processing errors"
when: errors # Empty list is falsy
Use Length in Loops
- name: Show progress
ansible.builtin.debug:
msg: "Processing {{ ansible_loop.index }} of {{ my_list | length }}: {{ item }}"
loop: "{{ my_list }}"
loop_control:
extended: true
Essential Jinja2 List Filters
Beyond length, here are the most useful list filters in Ansible:
Sorting
# Sort ascending
msg: "{{ my_list | sort }}"
# Sort descending
msg: "{{ my_list | sort(reverse=true) }}"
# Sort by attribute
msg: "{{ users | sort(attribute='name') }}"
Joining
# Join with comma
msg: "{{ packages | join(', ') }}"
# Output: "nginx, postgresql, redis"
# Join for command line
msg: "apt install {{ packages | join(' ') }}"
Filtering
# Select items matching criteria
msg: "{{ numbers | select('greaterthan', 5) | list }}"
# Reject items
msg: "{{ packages | reject('equalto', 'redis') | list }}"
# Select by attribute
msg: "{{ users | selectattr('active', 'equalto', true) | list }}"
Extracting
# Get unique values
msg: "{{ tags | unique }}"
# Get first/last
msg: "{{ my_list | first }}"
msg: "{{ my_list | last }}"
# Flatten nested lists
msg: "{{ nested_list | flatten }}"
# Extract attribute from list of dicts
msg: "{{ users | map(attribute='name') | list }}"
Set Operations
# Union (combine unique)
msg: "{{ list1 | union(list2) }}"
# Intersection (common items)
msg: "{{ list1 | intersect(list2) }}"
# Difference
msg: "{{ list1 | difference(list2) }}"
# Symmetric difference
msg: "{{ list1 | symmetric_difference(list2) }}"
Math on Lists
# Sum
msg: "{{ numbers | sum }}"
# Min/Max
msg: "{{ numbers | min }}"
msg: "{{ numbers | max }}"
Similar Attribute Errors
The same pattern applies to other common mistakes:
# WRONG # CORRECT
{{ my_list.sort() }} {{ my_list | sort }}
{{ my_list.reverse() }} {{ my_list | reverse | list }}
{{ my_string.upper() }} {{ my_string | upper }}
{{ my_string.lower() }} {{ my_string | lower }}
{{ my_string.strip() }} {{ my_string | trim }}
{{ my_dict.keys() }} {{ my_dict | dict2items | map(attribute='key') | list }}
{{ my_list.append('x') }} # Not possible — use: {{ my_list + ['x'] }}
Real-World Patterns
Dynamic Inventory Sizing
- name: Calculate worker count
ansible.builtin.set_fact:
worker_count: "{{ [ansible_processor_vcpus, groups['workers'] | length] | min }}"
Validate Input Lists
- name: Ensure at least 3 DNS servers configured
ansible.builtin.assert:
that:
- dns_servers | length >= 3
fail_msg: "Need at least 3 DNS servers, got {{ dns_servers | length }}"
Batch Processing
- name: Process in batches of 10
ansible.builtin.debug:
msg: "Batch {{ ansible_loop.index }}: {{ item }}"
loop: "{{ my_list | batch(10) | list }}"
loop_control:
extended: true
Related Articles
- Ansible Jinja2 Length Filter Guide
- Ansible Debug Module Guide
- Ansible Error Handling Guide
- Mastering Dynamic Variable Creation with set_fact
- Ansible Magic Variables Reference
- Ansible Best Practices Guide
- Ansible Ternary Filter
Conclusion
The list object has no attribute length error has a one-line fix: replace .length with | length. The underlying principle is simple — Jinja2 uses filters (pipe syntax) for operations that Python handles with methods (dot syntax). Once you internalize this pattern, you'll avoid an entire class of Ansible templating errors: use | sort not .sort(), | upper not .upper(), | join not .join().