That moment when your playbook fails with The task includes an option with an undefined variable is frustrating. You defined the variable, or at least you thought you did. The error message doesn't always make it clear where things went wrong.
Let me show you how to systematically diagnose and fix undefined variable errors.
Understanding the Error
A typical undefined variable error looks like this:
TASK [Deploy application] *****************************************************
fatal: [webserver01]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'app_version' is undefined\n\nThe error appears to be in '/home/user/playbooks/deploy.yml': line 15, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: Deploy application\n ^ here\n"}The key information: 'app_version' is undefined. Ansible looked for this variable and couldn't find it in any scope.
Step 1: Check Variable Definition Location
First, verify where you expected the variable to come from. Ansible variables can be defined in many places:
# Check if it's in your playbook
grep -r "app_version" playbooks/Common locations:
- group_vars/all.yml
- group_vars/webservers.yml
- host_vars/webserver01.yml
- Playbook vars: section
- Role defaults/main.yml or vars/main.yml
- Command line with -e
Step 2: Debug Variable Values
Use the debug module to inspect what Ansible actually sees:
- name: Debug variable value
debug:
var: app_versionRun it:
ansible-playbook -i inventory deploy.yml -t debugIf the output shows app_version: VARIABLE IS NOT DEFINED!, you know Ansible cannot find it anywhere.
For a comprehensive view of all variables for a host:
- name: Show all variables
debug:
var: varsOr use the setup module to see facts:
ansible webserver01 -i inventory -m setup | grep -i appStep 3: Understand Variable Precedence
Ansible has 22 levels of variable precedence. The most important ones from lowest to highest:
- 1.Role defaults (
roles/x/defaults/main.yml) - 2.Inventory file or script group vars
- 3.Inventory group_vars/all.yml
- 4.Playbook group_vars
- 5.Inventory host_vars
- 6.Playbook host_vars
- 7.Host facts
- 8.Play vars
- 9.Play vars_prompt
- 10.Role vars (
roles/x/vars/main.yml) - 11.Block vars
- 12.Task vars
- 13.Include_vars
- 14.Role params
- 15.Extra vars (
-e "key=value")
This means if you define app_version in both group_vars/all.yml and pass -e "app_version=2.0", the extra var wins.
To see the effective value considering precedence:
- name: Show variable with all sources
debug:
msg: "app_version is {{ app_version }}"
vars:
app_version: "override"Step 4: Fix Missing Variables with Defaults
The safest approach for variables that might not be defined is using the default filter:
- name: Deploy with default version
debug:
msg: "Deploying version {{ app_version | default('latest') }}"For complex defaults:
- name: Configure application
template:
src: app.conf.j2
dest: /etc/app/app.conf
vars:
db_host: "{{ db_host | default('localhost') }}"
db_port: "{{ db_port | default(5432) }}"Step 5: Handle Registered Variables
If the variable comes from a previous task's register, ensure that task runs first:
```yaml - name: Get current version shell: cat /opt/app/version.txt register: current_version changed_when: false
- name: Show version
- debug:
- var: current_version.stdout
`
If the first task fails or is skipped, current_version won't be defined properly. Always check:
- name: Show version safely
debug:
msg: "Current version: {{ current_version.stdout | default('unknown') }}"
when: current_version is definedStep 6: Check Inventory Variable Syntax
Inventory variables have specific syntax requirements. In INI format:
```ini [webservers] webserver01 app_version=1.2.3
[webservers:vars] env=production ```
In YAML format:
all:
children:
webservers:
hosts:
webserver01:
app_version: "1.2.3"
vars:
env: productionA common mistake is mixing formats or having syntax errors that silently fail:
ansible-inventory -i inventory --listThis validates your inventory and shows the parsed variables.
Step 7: Handle Nested Dictionary Access
Accessing nested undefined variables requires special handling:
# This fails if config is undefined
- name: Bad nested access
debug:
msg: "{{ config.database.host }}"Use the | default({}) pattern:
- name: Safe nested access
debug:
msg: "{{ (config | default({})).get('database', {}).get('host', 'localhost') }}"Or in Jinja2:
- name: Safe nested access with Jinja
debug:
msg: "{{ config.database.host | default('localhost') }}"
when: config is defined and config.database is definedStep 8: Debug Variable Scope Issues
Sometimes variables are defined but in the wrong scope. Use ansible.builtin.vars lookup:
- name: Check if variable exists in any scope
debug:
msg: "Variable exists: {{ lookup('vars', 'app_version', default='not found') }}"To list all variables at runtime:
ansible-playbook deploy.yml --stepThen at the step prompt, you can inspect variables.
Step 9: Conditional Variable Definition
Define variables conditionally based on other variables:
- name: Set version based on environment
set_fact:
app_version: "{{ '2.0.0' if env == 'production' else '2.0.0-dev' }}"
when: app_version is not definedOr use a dedicated variables file:
- name: Load environment variables
include_vars: "{{ env }}.yml"Where production.yml contains:
app_version: "2.0.0"
db_host: "prod-db.example.com"Quick Verification
After fixes, verify your variable is properly defined:
ansible webserver01 -i inventory -m debug -a "var=app_version"Or test the entire playbook with check mode:
ansible-playbook -i inventory deploy.yml --checkBest Practices
- 1.Use role defaults for fallback values:
# roles/app/defaults/main.yml
app_version: "latest"
app_port: 8080- 1.Validate required variables at play start:
- name: Validate required variables
assert:
that:
- app_version is defined
- db_host is defined
fail_msg: "Required variables are not defined"
success_msg: "All required variables are present"- 1.Document expected variables in playbook comments:
# Required variables:
# - app_version: Version to deploy
# - deploy_target: Target environment (staging/production)Undefined variable errors are almost always simple oversights. The key is understanding where Ansible looks for variables and ensuring your definitions are in the right place with the right precedence.