Fix Ansible Jinja2 "UndefinedError"

The Error

fatal: [webserver]: FAILED! => {
    "msg": "The task includes an option with an undefined variable. 
    The error was: 'my_variable' is undefined"
}

Solution 1: Use the default Filter

# Provide a fallback value
- debug:
    msg: "Port is {{ http_port | default(8080) }}"

# Default to empty string
- debug:
    msg: "Optional: {{ optional_var | default('') }}"

# Default to omit (skip the parameter entirely)
- ansible.builtin.user:
    name: deploy
    groups: "{{ extra_groups | default(omit) }}"

Solution 2: Check Before Using

- debug:
    msg: "Value: {{ my_var }}"
  when: my_var is defined

- debug:
    msg: "Not set"
  when: my_var is not defined

Solution 3: Set Variable Defaults in Role

# roles/myrole/defaults/main.yml
http_port: 8080
enable_ssl: false
app_user: www-data

Solution 4: Use vars with Defaults

- hosts: webservers
  vars:
    http_port: "{{ custom_port | default(80) }}"
    app_env: "{{ environment | default('production') }}"
  tasks:
    - debug:
        msg: "Running on port {{ http_port }} in {{ app_env }}"

Solution 5: Make Variables Mandatory

# Fail fast with a clear message
- assert:
    that:
      - database_host is defined
      - database_password is defined
    fail_msg: "database_host and database_password must be defined"

# Or use mandatory filter
- debug:
    msg: "DB: {{ database_host | mandatory }}"

Common Causes

Variable Scope Issues

# Variable set in one play is NOT available in another
- hosts: db
  tasks:
    - set_fact:
        db_ip: "{{ ansible_default_ipv4.address }}"

- hosts: web
  tasks:
    # WRONG — db_ip is not in scope
    - debug:
        msg: "{{ db_ip }}"
    
    # RIGHT — access from the other host
    - debug:
        msg: "{{ hostvars['db']['db_ip'] }}"

Typos

# Check your variable names carefully
- debug:
    msg: "{{ http_prot }}"  # ← typo! should be http_port

Missing group_vars/host_vars

# Make sure the file exists and is valid YAML
cat inventory/group_vars/webservers.yml

Conclusion

Always use | default() for optional variables. Use is defined checks for conditional logic. Set defaults in roles/*/defaults/main.yml for role variables.