Skip to main content

Debugging Template Errors

To troubleshoot template issues in this project, you can use built-in tools for inspecting context data, identifying syntax errors with precise positioning, and tracing template loading failures.

Inspecting Template Context

To see all variables currently available in your template's context, use the {% debug %} tag. This is implemented by the DebugNode class in django/template/defaulttags.py.

<!-- In your template -->
<div class="debug-info">
{% debug %}
</div>

When settings.DEBUG is True, the DebugNode.render method uses pprint to output a formatted list of every variable in the context, followed by a list of all imported Python modules.

# django/template/defaulttags.py

class DebugNode(Node):
def render(self, context):
if not settings.DEBUG:
return ""

from pprint import pformat

output = [escape(pformat(val)) for val in context]
output.append("\n\n")
output.append(escape(pformat(sys.modules)))
return "".join(output)

Note: If settings.DEBUG is False, the {% debug %} tag renders an empty string and performs no inspection.

Locating Syntax Errors

When a TemplateSyntaxError occurs, Django provides the exact line and character position of the error if the template engine is in debug mode. This is made possible by the DebugLexer in django/template/base.py.

The Template class automatically selects the DebugLexer when self.engine.debug is enabled:

# django/template/base.py

def compile_nodelist(self):
if self.engine.debug:
lexer = DebugLexer(self.source)
else:
lexer = Lexer(self.source)

tokens = lexer.tokenize()
# ... parsing continues

Unlike the standard Lexer, the DebugLexer tracks the start and end positions of every token using regex matches:

# django/template/base.py

class DebugLexer(Lexer):
def _tag_re_split_positions(self):
last = 0
for match in tag_re.finditer(self.template_string):
start, end = match.span()
yield last, start
yield start, end
last = end
yield last, len(self.template_string)

def tokenize(self):
# ...
for token_string, position in self._tag_re_split():
if token_string:
result.append(self.create_token(token_string, position, lineno, in_tag))
return result

This metadata allows the debug view to highlight the specific part of the template that caused the TemplateSyntaxError.

Troubleshooting Missing Templates

If a template cannot be found, Django raises a TemplateDoesNotExist exception. This exception is designed to help you trace which paths were searched across different backends.

In django/template/loader.py, the get_template function catches individual failures and aggregates them into a chain:

# django/template/loader.py

def get_template(template_name, using=None):
chain = []
engines = _engine_list(using)
for engine in engines:
try:
return engine.get_template(template_name)
except TemplateDoesNotExist as e:
chain.append(e)

raise TemplateDoesNotExist(template_name, chain=chain)

The TemplateDoesNotExist class stores this chain and a tried list (containing origins and statuses), which the Django debug page uses to render the "Template-loader postmortem."

# django/template/exceptions.py

class TemplateDoesNotExist(Exception):
def __init__(self, msg, tried=None, backend=None, chain=None):
self.backend = backend
self.tried = tried if tried is not None else []
self.chain = chain if chain is not None else []
super().__init__(msg)

Automatic Template Reloading

During development, the project automatically detects changes to template files and resets the loaders so you don't have to restart the server. This logic is handled in django/template/autoreload.py.

The system watches directories returned by get_template_directories() and triggers a reset when a non-Python file changes:

# django/template/autoreload.py

@receiver(file_changed, dispatch_uid="template_loaders_file_changed")
def template_changed(sender, file_path, **kwargs):
if file_path.suffix == ".py":
return # Python files are handled by the standard reloader
for template_dir in get_template_directories():
if template_dir in file_path.parents:
reset_loaders()
return True

The reset_loaders() function iterates through all configured DjangoTemplates backends and calls loader.reset() on each of their template loaders, clearing any cached templates.