Fix Ansible "undefined variable" Error: Variable Scope and Precedence
By Luca Berton · Published 2024-01-01 · Category: troubleshooting
How to fix Ansible undefined variable error. Understand variable scope, precedence, default values, and common causes. Complete troubleshooting guide.
Fix Ansible "undefined variable" Error: Variable Scope and Precedence
The "undefined variable" error is one of the most common Ansible issues. It means a variable is referenced but hasn't been defined in the current scope.
See also: Ansible Conflicting Action Statements Error: Causes and Fixes
Common Causes
1. Variable Not Defined
# ❌ Error: 'app_version' is undefined
- name: Deploy app
ansible.builtin.debug:
msg: "Deploying {{ app_version }}"
# ✅ Fix: Define the variable
- hosts: webservers
vars:
app_version: "2.1.0"
tasks:
- name: Deploy app
ansible.builtin.debug:
msg: "Deploying {{ app_version }}"
2. Typo in Variable Name
vars:
database_host: db.example.com
tasks:
# ❌ Typo: databse_host
- ansible.builtin.debug:
msg: "{{ databse_host }}"
# ✅ Correct spelling
- ansible.builtin.debug:
msg: "{{ database_host }}"
3. Variable from Another Host
# ❌ Can't access another host's registered variable directly
- hosts: databases
tasks:
- ansible.builtin.command: pg_isready
register: db_status
- hosts: webservers
tasks:
# This fails — db_status is on database hosts
- ansible.builtin.debug:
msg: "{{ db_status }}"
# ✅ Fix: Use hostvars
- ansible.builtin.debug:
msg: "{{ hostvars['db01']['db_status'] }}"
4. Conditional Variable
# ❌ Variable only defined in one branch
- ansible.builtin.set_fact:
result: success
when: some_condition
- ansible.builtin.debug:
msg: "{{ result }}" # Fails if some_condition was false
# ✅ Fix: Use default filter
- ansible.builtin.debug:
msg: "{{ result | default('not set') }}"
Best Practices to Prevent Undefined Variables
# 1. Always use defaults
"{{ my_var | default('fallback_value') }}"
"{{ my_list | default([]) }}"
"{{ my_dict | default({}) }}"
# 2. Use default(omit) for optional parameters
- ansible.builtin.user:
name: "{{ user_name }}"
groups: "{{ user_groups | default(omit) }}"
# 3. Validate inputs with assert
- ansible.builtin.assert:
that:
- app_version is defined
- env is defined
fail_msg: "Required variables not set"
# 4. Set role defaults
# roles/myrole/defaults/main.yml
app_port: 8080
app_env: production
log_level: info
See also: Ansible Permission Denied on Remote Temp Path: Fix Every Cause
FAQ
Why does Ansible say my variable is undefined?
The variable hasn't been defined in the current scope — it may be missing from vars, inventory, var files, or not passed via -e. Check spelling, scope (host vs play), and variable precedence.
How do I set a default value for undefined variables?
Use the default Jinja2 filter: {{ my_var | default('fallback') }}. This returns 'fallback' if my_var is not defined.
How do I check if a variable is defined?
Use when: my_var is defined in conditionals, or {{ my_var | default(omit) }} to skip optional parameters entirely.
Related Articles
• Ansible Variables: Complete Guide • Ansible Facts Guide • Ansible Playbook GuideSee also: Ansible Template Error While Templating String: Fix Every Jinja2 Error
Variable Precedence Order
Ansible has 22 levels of variable precedence. Here are the most commonly used (highest wins):
| Priority | Source | Example |
|---|---|---|
| 1 (highest) | Extra vars (-e) | ansible-playbook -e "var=value" |
| 2 | Task vars | vars: in a task |
| 3 | Block vars | vars: in a block |
| 4 | Role/include params | roles: - role: x, var: val |
| 5 | Play vars | vars: in a play |
| 6 | Host vars (inventory) | host_vars/hostname.yml |
| 7 | Group vars (inventory) | group_vars/groupname.yml |
| 8 (lowest) | Role defaults | roles/x/defaults/main.yml |
Safe Variable Access with Defaults
# Use the default filter to avoid undefined errors
- name: Safe variable access
ansible.builtin.debug:
msg: "Value: {{ my_var | default('fallback_value') }}"
# Use mandatory filter to fail with clear message
- name: Require a variable
ansible.builtin.debug:
msg: "Value: {{ required_var | mandatory }}"
# Check if variable is defined
- name: Conditional on variable
ansible.builtin.debug:
msg: "Variable is: {{ optional_var }}"
when: optional_var is defined
Common Causes and Fixes
# ❌ Variable defined in one play, used in another
- hosts: webservers
tasks:
- ansible.builtin.set_fact:
my_result: "done"
- hosts: dbservers
tasks:
- ansible.builtin.debug:
msg: "{{ my_result }}" # UNDEFINED!
# ✅ Use hostvars to access cross-host variables
- hosts: dbservers
tasks:
- ansible.builtin.debug:
msg: "{{ hostvars['web01']['my_result'] }}"
FAQ
Why is my variable undefined when I defined it in another play?
Variables set with set_fact or register are scoped to the host they run on. To access them from another host, use hostvars['hostname']['variable_name'].
How do I set a global variable accessible everywhere?
Use extra vars (-e), or define the variable in group_vars/all.yml which applies to every host.
What is the difference between vars and defaults in roles?
defaults/main.yml has the lowest precedence — easily overridden by anything. vars/main.yml has higher precedence and is harder to override. Use defaults for values users should customize.
Category: troubleshooting