Debugging Templates
When templates don't render as expected, here's how to diagnose and fix the problem.
Inspecting variables
Print a variable's type and value:
jinja
DEBUG: {{ some_var }} (type: {{ some_var | typeof }} )
The typeof filter returns: "string", "number", "none", "list", "dict", "bool".
Check if a variable is defined:
jinja
{% if some_var is defined %}
Has value: {{ some_var }}
{% else %}
Variable is undefined
{% endif %}
Available context
In page templates (page.html)
jinja
{{ page . title }} {# Page title #}
{{ page . content }} {# Rendered HTML body #}
{{ page . permalink }} {# URL path like "/guide/intro" #}
{{ page . weight }} {# Sort order #}
{{ page . toc }} {# Table of contents (list of headings) #}
{{ page . ancestors }} {# Breadcrumb chain (list of sections) #}
{{ page . last_updated }} {# Last modification timestamp #}
{{ page . extra }} {# Custom frontmatter fields #}
{{ section . title }} {# Parent section #}
{{ section . pages }} {# Sibling pages #}
In section templates (section.html, index.html)
jinja
{{ section . title }} {# Section title #}
{{ section . content }} {# Rendered HTML body from _index.md #}
{{ section . permalink }} {# URL path #}
{{ section . pages }} {# Pages in this section (sorted by weight) #}
{{ section . subsections }} {# Child sections #}
{{ section . toc }} {# Table of contents #}
{{ section . extra }} {# Custom frontmatter fields #}
Global variables (all templates)
jinja
{{ config . title }} {# Site title #}
{{ config . base_url }} {# Base URL #}
{{ current_path }} {# Current page's path #}
{{ root }} {# Root section (for navigation) #}
{{ data }} {# Custom data files (if any) #}
Common gotchas
Lists vs single values
Iterating over something that isn't a list:
jinja
{# This fails if page.extra.tags is a string, not a list #}
{% for tag in page . extra . tags %}
{{ tag }}
{% endfor %}
{# Check first #}
{% if page . extra . tags is defined %}
{% for tag in page . extra . tags %}
{{ tag }}
{% endfor %}
{% endif %}
None vs empty string vs undefined
These are all different:
jinja
{% if value is undefined %} {# Variable doesn't exist at all #}
{% if value is defined %} {# Variable exists (might be none/empty) #}
{% if value %} {# Truthy: not none, not empty, not 0 #}
{% if value is empty %} {# Empty string "" or empty list [] #}
Use default filter for fallbacks:
jinja
{{ page . description | default ( value = "No description" ) }}
Filter order matters
Filters apply left to right:
jinja
{{ items | first | upper }} {# Get first, then uppercase it #}
{{ items | sort | first }} {# Sort first, then get first #}
Accessing nested fields
Use dot notation:
jinja
{{ page . extra . author }}
{{ section . subsections | first | attr ( name = "title" ) }}
Reading error messages
Dodeca provides detailed error diagnostics. Example:
Error: template::unknown_field
× Unknown field `titl` on object
╭─[templates/page.html:5:1]
5 │ <h1>{{ page.titl }}</h1>
· ──┬─
· ╰── unknown field
╰────
help: Available fields: title, content, permalink, path, weight, toc, ancestors, last_updated, extra
The error shows:
- Error type:
unknown_field- you're accessing something that doesn't exist - Location:
templates/page.html:5- file and line number - Context: The actual template code with the problem highlighted
- Help: Available alternatives
Template inheritance issues
When using {% extends "base.html" %}:
- The child template must define blocks that exist in the parent
- Content outside blocks is ignored in child templates
- Use
{{ super() }}to include parent block content
jinja
{% extends "base.html" %}
{% block content %}
{{ super () }} {# Include parent's content block first #}
<p>Additional content</p>
{% endblock %}
Useful filters for debugging
jinja
{{ value | typeof }} {# Get type name #}
{{ items | length }} {# Count items #}
{{ items | first }} {# First element #}
{{ items | last }} {# Last element #}
{{ text | escape }} {# HTML-escape for safe display #}
Quick reference: available tests
Use with is in conditions:
jinja
{% if value is defined %}
{% if value is undefined %}
{% if value is string %}
{% if value is number %}
{% if value is empty %}
{% if value is odd %}
{% if value is even %}
{% if value is truthy %}
{% if path is starting_with ( "/admin" ) %}
{% if text is containing ( "search" ) %}