DebugUndefinedErrorexceptions.
The Error
UndefinedError: Undefined variable 'usre' in page.html:5
Common Causes
Typo in variable name
{# ❌ Typo #}
{{ usre.name }}
{# ✅ Correct #}
{{ user.name }}
Check spelling against what's passed to render().
Variable not passed to template
# ❌ Missing variable
template.render(title="Hello")
# ✅ Include all needed variables
template.render(title="Hello", user=current_user)
Ensure all template variables are passed in render().
Wrong attribute name
{# ❌ Wrong attribute #}
{{ user.nmae }}
{# ✅ Correct attribute #}
{{ user.name }}
Verify object attributes match your code.
Nested object is None
{# ❌ parent might be None #}
{{ page.parent.title }}
{# ✅ Check first #}
{% if page.parent %}
{{ page.parent.title }}
{% end %}
Use conditional checks or defaultfilter.
Solutions
Use default Filter
{{ user.nickname | default("Anonymous") }}
{{ config.timeout | default(30) }}
Check with is defined
Theis defined test works on attribute chains, not just top-level variables. If any part of the chain is missing, the result is undefined:
{% if user is defined %}
{{ user.name }}
{% else %}
Guest
{% end %}
Attribute Chains
{# Checks if pokemon has a "name" attribute — not just if pokemon exists #}
{% if pokemon.name is defined %}
{{ pokemon.name }}
{% end %}
{# Works with dict keys too #}
{% if settings.theme is defined %}
Theme: {{ settings.theme }}
{% end %}
{# Deep chains #}
{% if page.author.avatar is defined %}
<img src="{{ page.author.avatar }}">
{% end %}
Undefined Sentinel
Missing attribute access returns an_Undefinedsentinel (not an empty string). The sentinel is:
- Falsy —
{% if pokemon.name %}works as a guard - Stringifies to
""—{{ pokemon.name }}renders nothing when undefined - Iterable — yields nothing, so
{% for x in missing_attr %}produces no output
Theis defined and is undefined tests work correctly on attribute chains (e.g. {% if pokemon.name is defined %}), making intent explicit. See Tests Reference for the full test list.
Optional Chaining Pattern
{% if post and post.author %}
{{ post.author.name }}
{% end %}
Safe Navigation
{{ user | default({}) | attr("name") | default("Unknown") }}
Debug Tips
Print Available Variables
# In Python
print(context.keys())
Use debug Filter
{{ user | debug }}
Output (to stderr):
DEBUG: <User>
.name = 'Alice'
.email = 'alice@example.com'
Check Template Context
def render_debug(template_name, **context):
print(f"Rendering {template_name}")
print(f"Context keys: {list(context.keys())}")
return env.render(template_name, **context)
Strict Undefined Mode
Enablestrict_undefined=Trueon your Environment to catch attribute typos immediately instead of getting silent empty output:
env = Environment(
loader=FileSystemLoader("templates/"),
strict_undefined=True,
)
With strict mode, {{ user.typo }} raises UndefinedErrorimmediately with a descriptive message ("Undefined attribute 'typo'") instead of rendering as an empty string. Error messages distinguish between variable, attribute, and key lookups.
This is recommended for development and CI. In production, you may prefer the lenient default to avoid breaking pages on missing data.
Prevention
Type Hints for Context
from dataclasses import dataclass
@dataclass
class PageContext:
title: str
user: User
items: list[Item]
# IDE will catch missing fields
context = PageContext(title="Hello", user=user, items=items)
template.render(**asdict(context))
Template Validation
def validate_context(context, required):
missing = [k for k in required if k not in context]
if missing:
raise ValueError(f"Missing: {missing}")
See Also
- Error Handling — Exception types
- Variables — Variable access patterns
- Filters — The default filter