Your playbook runs but tasks execute when they shouldn't, or don't execute when they should. Condition evaluation errors in Ansible's when clauses cause unpredictable behavior and silent failures.

Understanding the Error

Condition errors manifest differently:

bash
fatal: [webserver01]: FAILED! => {"msg": "The conditional check 'item == true' failed. The error was: error while evaluating conditional (item == true): 'item' is undefined"}

Or subtler - tasks running incorrectly:

yaml
- name: Should skip
  debug:
    msg: "This should not run"
  when: False  # Actually true due to string comparison

Or syntax errors:

bash
ERROR! 'when' clause 'item = value' is invalid: must be Jinja2 expression

Step 1: Understand When Syntax

when accepts Jinja2 expressions:

```yaml # CORRECT - Jinja2 expression when: ansible_os_family == "Debian"

# WRONG - assignment syntax when: ansible_os_family = "Debian" ```

The result must evaluate to a boolean-like value: - true, yes, 1 - runs task - false, no, 0, empty string - skips task

Step 2: Fix String vs Boolean Comparison

A common pitfall - string comparison with boolean:

```yaml # WRONG - string "true" vs boolean True when: result == true # If result is string "true", this fails

# CORRECT - type-aware comparison when: result == True # Boolean True when: result == "true" # String "true" when: result | bool # Convert to boolean ```

Check what type your variable is:

yaml
- name: Debug variable type
  debug:
    msg: "Type: {{ result | type_debug }}"

Output: str or bool or int.

Step 3: Handle Undefined Variables

When clauses fail if variables are undefined:

```yaml # WRONG - fails if var undefined when: my_variable == "value"

# CORRECT - check defined first when: my_variable is defined and my_variable == "value"

# Or use default when: (my_variable | default("")) == "value" ```

Order matters - check defined before using:

```yaml # WRONG - fails because undefined check comes after comparison when: my_variable == "value" and my_variable is defined

# CORRECT - check defined first when: my_variable is defined and my_variable == "value" ```

Step 4: Fix Comparison Operators

YAML has specific syntax requirements for comparisons:

```yaml # CORRECT comparison operators when: value == "string" when: value != "string" when: value > 10 when: value < 10 when: value >= 10 when: value <= 10

# WRONG in YAML when: value = "string" # Assignment, not comparison ```

For equality:

```yaml # String comparison when: ansible_os_family == "Debian"

# Numeric comparison when: port == 8080

# Boolean comparison when: enabled | bool ```

Step 5: Handle Multiple Conditions

Combine conditions with and, or:

```yaml # AND - both must be true when: ansible_os_family == "Debian" and ansible_distribution_version == "20.04"

# OR - either must be true when: ansible_os_family == "Debian" or ansible_os_family == "RedHat"

# Parentheses for complex logic when: (ansible_os_family == "Debian" and version == "20.04") or force_install ```

YAML lists for conditions:

yaml
# List acts as AND
when:
  - ansible_os_family == "Debian"
  - ansible_distribution_version == "20.04"

Step 6: Fix Jinja2 Tests

Jinja2 tests use is:

```yaml # CORRECT - Jinja2 tests when: variable is defined when: variable is undefined when: variable is none when: variable is string when: variable is number when: path is exists when: path is directory when: path is file

# WRONG when: defined(variable) ```

Common tests:

```yaml # Check file exists when: "/etc/nginx/nginx.conf" is exists

# Check variable is truthy when: my_var is truthy

# Check variable in list when: item in ["web1", "web2"] ```

Step 7: Handle Registered Variable Conditions

Registered variables have specific structure:

```yaml - name: Check service command: systemctl is-active nginx register: service_status changed_when: false failed_when: false

  • name: Start if not active
  • service:
  • name: nginx
  • state: started
  • when: service_status.rc != 0
  • `

Common registered checks:

```yaml # Check if task changed when: previous_task.changed

# Check if task succeeded when: previous_task is succeeded

# Check command output when: "'success' in result.stdout" when: result.stdout | search('pattern') ```

Step 8: Fix String Pattern Matching

Search strings in output:

```yaml # CORRECT - search in output when: "'error' in result.stdout"

# Using regex when: result.stdout | regex_search('pattern')

# WRONG when: result.stdout contains 'error' # 'contains' not valid ```

For regex:

yaml
when: result.stdout is search('pattern')
when: result.stdout is match('exact_pattern')

Step 9: Handle List Conditions

Check items in lists:

```yaml # CORRECT when: item in my_list when: my_list | length > 0 when: my_list is not empty

# WRONG when: my_list.contains(item) # Python syntax not valid ```

For dictionary keys:

yaml
when: "'key' in my_dict"
when: my_dict.key is defined

Step 10: Fix Numeric Comparisons

Numeric comparisons need proper types:

```yaml # CORRECT when: ansible_memtotal_mb > 1024 when: port | int == 8080

# WRONG - comparing string to int when: port == 8080 # If port is string "8080" ```

Convert types explicitly:

yaml
when: (value | int) > 100
when: (value | float) > 0.5

Step 11: Handle Ansible Facts in Conditions

Facts provide system information:

```yaml # CORRECT - using facts when: ansible_os_family == "RedHat" when: ansible_distribution_version | version_compare('7.0', '>=') when: ansible_architecture == "x86_64"

# Check IPv4 address when: ansible_default_ipv4.address | ipaddr('192.168.1.0/24') ```

Step 12: Debug Condition Evaluation

See what's happening:

```yaml - name: Debug condition debug: msg: "ansible_os_family is {{ ansible_os_family }}" when: ansible_os_family is defined

  • name: Show evaluated condition
  • debug:
  • msg: "Condition result: {{ ansible_os_family == 'Debian' }}"
  • `

Use verbose output:

bash
ansible-playbook playbook.yml -vv

Look for:

bash
static condition: False
skipping: [webserver01]

Quick Verification

Test condition behavior:

```yaml - hosts: localhost gather_facts: yes tasks: - name: Test condition debug: msg: "Condition met" when: ansible_os_family == "Debian"

  • name: Show condition result
  • debug:
  • msg: "{{ ansible_os_family == 'Debian' }}"
  • `

Run it:

bash
ansible-playbook test_condition.yml

Prevention Best Practices

  1. 1.Always check defined before using:
yaml
when: my_var is defined and my_var == "value"
  1. 1.Use type conversion explicitly:
yaml
when: (value | int) > threshold
when: value | bool
  1. 1.Quote string comparisons:
yaml
when: ansible_os_family == "Debian"
  1. 1.Use parentheses for complex logic:
yaml
when: (a == "x" and b == "y") or c
  1. 1.Debug conditions before relying on them:
yaml
- debug:
    var: my_condition_variable
- debug:
    msg: "{{ my_condition_variable == 'expected' }}"

Condition evaluation errors come from undefined variables, type mismatches, or Jinja2 syntax issues. Check variable existence, convert types explicitly, and debug condition results before relying on task execution.