Ansible Template Error While Templating String: Fix Every Jinja2 Error
By Luca Berton · Published 2024-01-01 · Category: installation
Fix every Ansible 'template error while templating string' with 15+ real examples. Covers undefined variables, type errors, missing filters, nested quotes.
"An error occurred while templating string" is the most common Jinja2 error in Ansible. The message is frustratingly vague — the actual cause could be undefined variables, type mismatches, bad syntax, or a dozen other things. This guide covers every variant with the exact fix.
Error Format
fatal: [host]: FAILED! => {
"msg": "An error occurred while templating string:
{{ some_expression }}. Error was a <class 'ansible.errors.AnsibleUndefinedVariable'>,
original message: 'variable_name' is undefined"
}
The key is the original message at the end — that tells you the real problem.
See also: Ansible Troubleshooting Installation Issues on macOS and Python
Error 1: Variable Is Undefined
Error
'my_variable' is undefined
Cause and Fix
# ❌ Variable doesn't exist
- ansible.builtin.debug:
msg: "{{ my_variable }}"
# ✅ FIX 1: Define the variable
- hosts: all
vars:
my_variable: "hello"
# ✅ FIX 2: Use default filter
- ansible.builtin.debug:
msg: "{{ my_variable | default('fallback') }}"
# ✅ FIX 3: Check before using
- ansible.builtin.debug:
msg: "{{ my_variable }}"
when: my_variable is defined
Error 2: Attribute Not Found on Dict
Error
'dict object' has no attribute 'nonexistent_key'
Cause and Fix
# ❌ Key doesn't exist in the dict
- ansible.builtin.debug:
msg: "{{ result.json.status }}"
# result.json doesn't have a 'status' key
# ✅ FIX 1: Use default
- ansible.builtin.debug:
msg: "{{ result.json.status | default('unknown') }}"
# ✅ FIX 2: Use bracket notation with default
- ansible.builtin.debug:
msg: "{{ result.json['status'] | default('unknown') }}"
# ✅ FIX 3: Debug the actual structure first
- ansible.builtin.debug:
var: result.json
See also: Ansible Conflicting Action Statements Error: Causes and Fixes
Error 3: String Cannot Be Converted to Int/Float
Error
could not convert string to float: 'not_a_number'
Cause and Fix
# ❌ Comparing string to int
- ansible.builtin.assert:
that: result.stdout > 100
# result.stdout is always a string
# ✅ FIX: Cast explicitly
- ansible.builtin.assert:
that: result.stdout | int > 100
# ✅ For float comparison
- ansible.builtin.assert:
that: result.stdout | float > 99.5
Error 4: Nested Quoting Issues
Error
unexpected char '#' at ...
template error while templating string
Cause and Fix
# ❌ YAML/Jinja2 quote collision
- ansible.builtin.debug:
msg: "{{ 'hello "world"' }}"
# ✅ FIX 1: Alternate quotes
- ansible.builtin.debug:
msg: "{{ 'hello \"world\"' }}"
# ✅ FIX 2: Use YAML literal block
- ansible.builtin.debug:
msg: >-
{{ 'hello "world"' }}
# ❌ Hash in string looks like YAML comment
- ansible.builtin.debug:
msg: "Color is {{ color }} #ffffff"
# ✅ FIX: Quote the whole thing
- ansible.builtin.debug:
msg: "Color is {{ color }} {{ '#ffffff' }}"
See also: Ansible Permission Denied on Remote Temp Path: Fix Every Cause
Error 5: Filter Not Found
Error
No filter named 'community.general.json_query'
Cause and Fix
# json_query requires jmespath
pip install jmespath
# Or in requirements.txt
pip install jmespath ansible
# Some filters require collections
# Install the collection first:
# ansible-galaxy collection install community.general
- ansible.builtin.debug:
msg: "{{ data | community.general.json_query('servers[*].name') }}"
Error 6: Recursive Templating
Error
recursive loop detected in template string
Cause and Fix
# ❌ Variable references itself
vars:
my_url: "https://{{ my_url }}/api"
# ✅ FIX: Use different variable names
vars:
base_url: "https://example.com"
api_url: "{{ base_url }}/api"
Error 7: Type Error in Filter
Error
'NoneType' object is not iterable
Cause and Fix
# ❌ Filtering a None/undefined value
- ansible.builtin.debug:
msg: "{{ missing_list | join(', ') }}"
# ✅ FIX: Default to empty list
- ansible.builtin.debug:
msg: "{{ missing_list | default([]) | join(', ') }}"
# ✅ For dict operations
- ansible.builtin.debug:
msg: "{{ missing_dict | default({}) | dict2items }}"
Error 8: Boolean Expression Issues
Error
Conditional check '...' failed. The error was: ...
Cause and Fix
# ❌ String treated as boolean
- ansible.builtin.debug:
msg: "enabled"
when: some_var # some_var might be the string "false"
# ✅ FIX: Explicit bool conversion
when: some_var | bool
# ❌ Comparing to string "true"
when: some_var == true # Fails if some_var is "true" (string)
# ✅ FIX
when: some_var | bool == true
Error 9: Whitespace in Jinja2
Error
expected token 'end of statement block', got 'string'
Cause and Fix
# ❌ Missing spaces around operators
- ansible.builtin.debug:
msg: "{{ var1+var2 }}"
# ✅ FIX: Add spaces
- ansible.builtin.debug:
msg: "{{ var1 + var2 }}"
# ❌ Extra braces
- ansible.builtin.debug:
msg: "{{{ variable }}}"
# ✅ FIX
- ansible.builtin.debug:
msg: "{{ variable }}"
Error 10: YAML Multiline String Issues
Error
could not find expected ':'
Cause and Fix
# ❌ Jinja2 in unquoted YAML value that starts with {
vars:
value: {{ something }}
# ✅ FIX: Always quote Jinja2 expressions
vars:
value: "{{ something }}"
# ❌ Multiline template with bad indentation
msg: "{{ items |
join(', ') }}"
# ✅ FIX: Use YAML multiline scalar
msg: >-
{{ items |
join(', ') }}
Error 11: ansible-core 2.19 Strict Templating
New in ansible-core 2.19 — stricter Jinja2 evaluation:
# ❌ BREAKS in 2.19 — truthy string in conditional
when: some_string_variable
# ✅ FIX: Explicit boolean test
when: some_string_variable | length > 0
# ❌ BREAKS in 2.19 — implicit truth test
when: result.stdout
# ✅ FIX
when: result.stdout | length > 0
# or
when: result.stdout != ""
See ansible-core 2.19 Templating Changes for the full list.
Debugging Tips
# 1. Enable verbose mode
ansible-playbook site.yml -vvv
# 2. Test expressions interactively
ansible localhost -m debug -a "msg={{ 'hello' | upper }}"
# 3. Dump all variables for a host
ansible host -m debug -a "var=hostvars[inventory_hostname]"
# 4. Use the 'type_debug' filter
- ansible.builtin.debug:
msg: "{{ my_var | type_debug }}"
# Shows: "str", "list", "dict", "NoneType", etc.
FAQ
How do I find which line causes the template error?
Use -vvv to see the full traceback. The error message includes the template string that failed. Search your playbook for that exact string. In roles, check defaults/main.yml and vars/main.yml for the variable definition.
Why does my template work in one play but fail in another?
Variables are scoped. A variable set in play 1 (via vars:) isn't available in play 2. Use set_fact (persists across plays), group_vars (inventory-level), or extra_vars (global) for cross-play variables.
How do I escape Jinja2 syntax in a template file?
Use {% raw %}...{% endraw %} to output literal {{ }} in template files:
{% raw %}
This {{ will not be templated }}
{% endraw %}
Can I test Jinja2 expressions without running a playbook?
Yes. Use ansible localhost -m debug -a "msg={{ expression }}" for quick tests, or ansible-playbook --check for full playbook validation without making changes.
Conclusion
Every Ansible template error traces back to: undefined variables (use default), type mismatches (use int/float/bool filters), quoting issues (always quote {{ }}), or missing dependencies (jmespath for json_query). Read the original message at the end of the error — it tells you exactly what went wrong.
Related Articles
• ansible-core 2.19 Templating Changes • Ansible Undefined Variable Error: 12 Fixes • Ansible Jinja2 Filters Reference • Ansible default() Filter GuideSee also
• Fix Ansible "undefined variable" Error: Variable Scope and PrecedenceCategory: installation