Fix Ansible 'item is a string and cannot be compared to an integer' in when Conditions

beginner๐Ÿ”ง Ansible2026-05-03| Ansible 2.9+, Python 3.x, any Linux/macOS control node

Error Message

The conditional check 'item > 5' failed. The error was: error while evaluating conditional (item > 5): 'item' is a string and cannot be compared to an integer
#ansible#jinja2#variables#type-error

The Error

You're looping over a list or using a variable in a when condition and Ansible throws:

The conditional check 'item > 5' failed. The error was: error while evaluating conditional (item > 5): 'item' is a string and cannot be compared to an integer

The playbook halts. Nothing runs. The maddening part? The value looks like a number โ€” it's just that Jinja2 disagrees.

Why This Happens

Ansible variables are loosely typed โ€” and that flexibility bites you here. Define a list in a vars file, inventory, or from a register result, and values often arrive as strings even when they look numeric:

vars:
  port_list:
    - "8080"
    - "443"
    - "3000"

Jinja2 sees "8080" and 5 as incompatible types. Python 3 โ€” which Ansible runs on โ€” refuses to silently coerce strings into numbers during comparisons. Python 2 let that slide; Python 3 does not.

Common sources of string-typed numbers:

  • YAML values wrapped in quotes: port: "8080"
  • Environment variables from lookup('env', ...)
  • Command output captured via register + stdout
  • Variables passed via --extra-vars on the CLI (always strings, no exceptions)
  • Remote facts like ansible_processor_count, depending on the host

Diagnosing the Problem

First, confirm what type Ansible actually sees. Drop a debug task in front of the failing condition:

- name: Debug item type
  debug:
    msg: "Value: {{ item }} | Type: {{ item | type_debug }}"
  loop: "{{ port_list }}"

If the output shows Type: str instead of Type: int, that's your culprit.

Checking a single variable works the same way:

- name: Check variable type
  debug:
    msg: "{{ my_count | type_debug }}"

Fix 1: Cast with the int Filter (Recommended)

Use the int filter โ€” it converts the string to an integer right inside the when expression, no restructuring needed:

- name: Only process ports above 1024
  debug:
    msg: "High port: {{ item }}"
  loop: "{{ port_list }}"
  when: item | int > 1024

The cast happens before the comparison runs. If a value can't be converted โ€” say, an empty string โ€” the filter defaults to 0, which prevents a crash rather than blowing up the run.

Need a different fallback? Set it explicitly:

when: item | int(default=0) > 1024

Fix 2: Remove Quotes from the Variable Definition

Got direct control over the variable definition? Drop the quotes:

# Before (strings)
port_list:
  - "8080"
  - "443"

# After (integers)
port_list:
  - 8080
  - 443

Unquoted numbers in YAML are always parsed as integers. Cleaner source data means simpler playbooks โ€” no filter juggling required downstream.

Fix 3: Cast at the Point of Registration

register results always come back as strings. Cast early โ€” ideally right in the when condition where you use it:

- name: Get number of running containers
  command: docker ps -q | wc -l
  register: container_count

- name: Warn if too many containers
  debug:
    msg: "Container count is high!"
  when: container_count.stdout | int > 10

If you'll reuse the value in multiple places, store the cast result in a fact:

- name: Set integer fact
  set_fact:
    container_count_int: "{{ container_count.stdout | int }}"

- name: Use integer fact in condition
  debug:
    msg: "High count: {{ container_count_int }}"
  when: container_count_int > 10

Fix 4: Extra Vars from the CLI

--extra-vars strings are always strings โ€” no matter what they look like. Cast them inside the playbook:

ansible-playbook site.yml --extra-vars "max_retries=5"
- name: Retry check
  debug:
    msg: "Too many retries"
  when: max_retries | int > 3

Alternatively, pass the value as JSON to preserve the type at the source:

ansible-playbook site.yml --extra-vars '{"max_retries": 5}'

Verifying the Fix

Run with -v and watch the task output:

ansible-playbook site.yml -v

A working fix looks like this:

TASK [Only process ports above 1024]
skipping: [localhost] => (item=443)
ok: [localhost] => (item=8080)

No more 'item' is a string. The condition skips 443 and runs on 8080, exactly as intended.

Still unsure the cast landed? Print the type explicitly after the filter:

- name: Confirm type after cast
  debug:
    msg: "Casted type: {{ item | int | type_debug }}"
  loop: "{{ port_list }}"

Expected output: Casted type: int

Quick Reference

  • {{ item | int }} โ€” cast string to integer (defaults to 0 on failure)
  • {{ item | float }} โ€” cast to float if needed
  • {{ item | string }} โ€” cast integer back to string
  • {{ item | type_debug }} โ€” print the actual Python type
  • Unquoted YAML numbers (8080) are integers; quoted ones ("8080") are strings

Lessons Learned

Nine times out of ten, the culprit is a quoted number in YAML or a value piped in from a shell command. Both arrive as strings. The int filter handles either case in-place โ€” one small addition, no playbook restructuring.

One habit worth building: stop quoting numbers in vars files. YAML treats 8080 as an integer automatically. Let it do that work, and half these type errors disappear before they start.

Related Error Notes