Ansible Error Guide: 'AnsibleUndefinedVariable' Exception During Task Execution
Fix Ansible's 'An exception occurred during task execution ... AnsibleUndefinedVariable' error: diagnose missing vars, scoping, typos, and unset facts in playbooks.
- #ansible
- #troubleshooting
- #errors
- #variables
Exact Error Message
fatal: [db-01]: FAILED! => {
"changed": false,
"msg": "An exception occurred during task execution. To see the full traceback, use -vvv. The error was: 'db_password' is undefined. 'db_password' is undefined"
}
You may also see the variant with the full class name:
"msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'db_password'"
Both point at the same root problem: a variable referenced in a task, template, or expression has no value at the moment Ansible tried to use it.
What the Error Means
When Ansible renders a task it resolves every {{ variable }} reference. If a referenced name is not defined anywhere in scope at that point in the play, Jinja2’s strict-undefined behavior raises AnsibleUndefinedVariable. Ansible wraps it as An exception occurred during task execution.
By default Ansible treats undefined variables as fatal (error_on_undefined_vars is on). This is intentional: silently substituting an empty string into a password, path, or hostname is far more dangerous than failing loudly.
Common Causes
- A typo in the variable name (
db_passwdvsdb_password). - The variable lives in a
vars_file,group_vars, orhost_varsthat was never loaded for this host or group. - A fact that has not been gathered yet (
gather_facts: false) but is referenced anyway. - Variable scoping: a
set_factran on one host but the variable is read on another, or ablock/rolelocal was used outside its scope. registered results referenced before the registering task ran (or after it was skipped bywhen).- A nested key that does not exist on a dict, surfacing as
'dict object' has no attribute '...'.
How to Reproduce the Error
Reference a variable you never define:
- hosts: db
gather_facts: false
tasks:
- name: Configure database
ansible.builtin.debug:
msg: "Connecting as {{ db_user }} with {{ db_password }}"
ansible-playbook db.yml -i inventory.ini --check --diff -vvv
fatal: [db-01]: FAILED! => {"changed": false, "msg": "An exception occurred during task execution. ... The error was: 'db_password' is undefined."}
A skipped-register variant: a task guarded by when: false never runs, then a later task reads its registered result and fails the same way.
Diagnostic Commands
Run with -vvv in check mode to get the full traceback and the exact undefined name:
ansible-playbook db.yml -i inventory.ini --check --diff -vvv
Dump everything Ansible knows about the host to see which vars are actually present:
ansible db-01 -i inventory.ini -m setup
Confirm what inventory-level variables resolve for the host and group:
ansible-inventory -i inventory.ini --host db-01
Check a single variable’s value quickly with debug:
ansible db-01 -i inventory.ini -m debug -a "var=db_password"
Step-by-Step Resolution
-
Read the exact undefined name. The error names the variable (or the missing attribute). Confirm spelling against where you think it is defined.
-
Find where it should be defined. Search your tree:
grep -rn "db_password" group_vars/ host_vars/ vars/ roles/
- Make sure the file is actually loaded.
group_vars/db.ymlonly loads for hosts in thedbgroup. Verify membership:
ansible-inventory -i inventory.ini --graph
-
Gather facts if you reference them. If a task uses
ansible_default_ipv4or similar, ensuregather_facts: trueor add an explicitsetuptask. -
Guard genuinely optional variables with a default so absence is intentional rather than fatal:
msg: "Connecting as {{ db_user | default('postgres') }}"
- Fix register/scope issues. Ensure the registering task is not skipped, or test for it:
when: result is defined and result.rc == 0
- Re-run in check mode and confirm the variable resolves before applying:
ansible-playbook db.yml -i inventory.ini --check --diff
Prevention and Best Practices
- Define required variables in
group_vars/host_varsand document them in the role’sdefaults/main.ymlso there is always a sensible fallback. - Use
| default(...)only for truly optional values; for required secrets, prefer failing loudly withassert. - Validate inputs early with a fail-fast
assertblock at the top of a role:
- ansible.builtin.assert:
that: db_password is defined
fail_msg: "db_password must be set"
- Keep variable names consistent and namespaced per role (
myapp_db_password) to avoid collisions and typos. - Do not reference facts when
gather_facts: false; gather explicitly or supply the value. - Lint with
ansible-lint, which flags many undefined-variable patterns before runtime.
Related Errors
template error while templating string— a Jinja2 syntax/evaluation error, not a missing variable.'dict object' has no attribute '...'— the variable exists but the nested key you indexed does not.The conditional check '...' failed— awhen:expression referencing an undefined variable.VARIABLE IS NOT DEFINED!— whatdebugprints when youvar=an undefined name, rather than a fatal error.
Frequently Asked Questions
Why does Ansible fail instead of using an empty value? error_on_undefined_vars defaults to true precisely so that an undefined password or path causes a loud failure instead of silently breaking your config. Leave it on.
My variable is in group_vars but still undefined. The host is probably not a member of that group. Check with ansible-inventory --graph and confirm the file name matches the group name.
How do I make a variable optional? Wrap each reference in {{ var | default('fallback') }}. For required values, use an assert so the run fails with a clear message instead of a cryptic traceback.
A registered variable is undefined. The task that should register it was skipped by a when: condition or never ran. Guard downstream usage with when: result is defined. For more variable and scoping patterns, see the Ansible guides.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.