Introduction
Ansible leverages Jinja2 templates for dynamic configurations, variable interpolation, and conditional logic in playbooks. Ansible Lint rule 207 (jinja[invalid]) detects invalid Jinja2 template syntax — expressions that would cause runtime failures when Ansible attempts to render them. This article covers every common pattern that triggers this error, how to fix each one, the auto-fix capability, and current limitations.
Understanding the Error
Rule 207 belongs to the basic profile and is tagged as formatting. It catches Jinja2 expressions that contain syntax errors — invalid characters, malformed filters, nested template markers, or broken conditional expressions.
Error Output Example
WARNING Listing 1 violation(s) that are fatal
jinja[invalid]: template error while templating string: unexpected char '&' at 3.
String: {{ & }}. unexpected char '&' at 3
playbook.yml:8 Task/Handler: Error 207
Rule Violation Summary
count tag profile rule associated tags
1 jinja[invalid] basic formatting
Failed: 1 failure(s), 0 warning(s) on 1 files.
Common Patterns That Trigger Error 207
Pattern 1: Invalid Characters in Expressions
Using characters that are not valid Python/Jinja2 operators:
# ❌ Invalid — '&' is not a Jinja2 operator
vars:
bar: "{{ & }}"
# ✅ Fixed — quote the literal string
vars:
bar: "{{ '&' }}"
Pattern 2: Nested Template Markers
Double curly braces inside an existing Jinja2 expression:
# ❌ Invalid — nested {{ }} creates a parsing error
vars:
result: "{{ {{ my_var }} }}"
# ✅ Fixed — no nesting needed, just reference the variable
vars:
result: "{{ my_var }}"
Pattern 3: Unbalanced Braces
Missing or extra curly braces:
# ❌ Invalid — missing closing braces
vars:
name: "{{ my_var }"
# ✅ Fixed
vars:
name: "{{ my_var }}"
Pattern 4: Invalid Filter Syntax
Misspelled filters or wrong filter arguments:
# ❌ Invalid — 'toint' is not a real filter
vars:
port: "{{ '8080' | toint }}"
# ✅ Fixed — correct filter name is 'int'
vars:
port: "{{ '8080' | int }}"
Pattern 5: Unclosed String Literals
# ❌ Invalid — unclosed string inside expression
vars:
greeting: "{{ 'hello }}"
# ✅ Fixed — close the string
vars:
greeting: "{{ 'hello' }}"
Pattern 6: Invalid Comparison Operators
# ❌ Invalid — '===' is not valid in Jinja2
when: "{{ my_var === 'value' }}"
# ✅ Fixed — use '==' (and remove {{ }} from when clause)
when: my_var == 'value'
Pattern 7: Missing Filter Name After Pipe
# ❌ Invalid — pipe with no filter
vars:
result: "{{ my_list | }}"
# ✅ Fixed — specify the filter
vars:
result: "{{ my_list | join(', ') }}"
Pattern 8: Invalid Arithmetic
# ❌ Invalid — consecutive operators
vars:
total: "{{ a + * b }}"
# ✅ Fixed
vars:
total: "{{ a * b }}"
Complete Error and Fix Example
Error Playbook
---
- name: Example error 207
hosts: all
tasks:
- name: Task with invalid template
ansible.builtin.debug:
msg: "Value is {{ & }}"
vars:
bar: "{{ & }}"
Running Lint
$ ansible-lint playbook.yml
WARNING Listing 2 violation(s) that are fatal
jinja[invalid]: template error while templating string: unexpected char '&' at 3.
playbook.yml:5 Task/Handler: Task with invalid template
jinja[invalid]: template error while templating string: unexpected char '&' at 3.
playbook.yml:8
Failed: 2 failure(s), 0 warning(s) on 1 files.
Fixed Playbook
---
- name: Example error 207
hosts: all
tasks:
- name: Task with valid template
ansible.builtin.debug:
msg: "Value is {{ '&' }}"
vars:
bar: "{{ '&' }}"
Auto-Fix with ansible-lint
Error 207 supports automatic fixing:
# Auto-fix invalid Jinja2 templates
ansible-lint --fix playbook.yml
The auto-fixer handles common cases like spacing issues and simple syntax corrections. However, it cannot fix all cases — particularly when the intended expression is ambiguous.
Working with Special Characters
When you need literal special characters in Jinja2 templates:
# Literal ampersand
msg: "{{ '&' }}"
# Literal curly braces — use raw block
msg: "{% raw %}{{ not_a_variable }}{% endraw %}"
# Literal percent sign
msg: "{{ '%' }}"
# HTML entities
msg: "{{ '&' }}"
The raw Block
For content that should not be processed by Jinja2:
- name: Write Jinja2 template file
ansible.builtin.copy:
dest: /etc/app/template.j2
content: |
{% raw %}
server_name {{ server_hostname }};
listen {{ http_port }};
{% endraw %}
Jinja2 Template Validation Tips
Test Templates Locally
# Use Python to validate Jinja2 syntax
python3 -c "
from jinja2 import Environment
env = Environment()
try:
env.parse('{{ my_var | default(\"hello\") }}')
print('Valid')
except Exception as e:
print(f'Invalid: {e}')
"
Use ansible-lint in CI/CD
# .github/workflows/lint.yml
- name: Lint playbooks
run: |
pip install ansible-lint
ansible-lint playbooks/ roles/ --strict
Check Templates Before Rendering
# Dry run to catch template errors
ansible-playbook playbook.yml --check --diff
Current Limitations
- Newline-formatted blocks: Jinja2 blocks with intentional newlines are not reformatted (assumed intentional formatting)
- Tilde operator: Blocks using
~as a binary operator are ignored (Python'sblackformatter doesn't support it) - Dot notation with numbers: Blocks like
{{ foo.0.bar }}may be ignored since Python/black don't allow it - Complex nested expressions: The auto-fixer may not resolve deeply nested or ambiguous templates
Related Rules
| Rule | Description |
|---|---|
jinja[spacing] | Jinja2 spacing issues (e.g., {{var}} → {{ var }}) |
jinja[invalid] | Invalid Jinja2 template syntax (this rule) |
no-jinja-when | Don't use {{ }} in when clauses |
template-instead-of-copy | Use template module when content has Jinja2 |
Related Articles
- Ansible Jinja2 Templates Guide
- Ansible Filter Plugins Guide
- Ansible Lint Guide
- Ansible changed_when failed_when Guide
Conclusion
Ansible Lint Error 207 (jinja[invalid]) catches invalid Jinja2 templates before they cause runtime failures. The most common triggers are invalid characters, nested template markers, unclosed strings, and misspelled filters. Use ansible-lint --fix for automatic correction, the {% raw %} block for literal template content, and integrate linting into your CI/CD pipeline to catch these errors early.