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.