Error Handling

Template errors, debugging, and stack traces

6 min read 1187 words

Kida provides clear error messages with source locations, error codes, and actionable hints.

Exception Types

Exception When Raised
TemplateError Base class for all template errors
TemplateSyntaxError Invalid template syntax
TemplateRuntimeError Error during template rendering
TemplateNotFoundError Template file not found (includes caller context)
UndefinedError Accessing undefined variable or attribute
SecurityError Sandbox policy violation

All exception classes are importable from the top-levelkidapackage:

from kida import (
    TemplateError,
    TemplateSyntaxError,
    TemplateRuntimeError,
    TemplateNotFoundError,
    UndefinedError,
    SecurityError,
)

Syntax Errors

from kida import Environment, TemplateSyntaxError

env = Environment()
try:
    template = env.from_string("{% if x %}")  # Missing end
except TemplateSyntaxError as e:
    print(e)
    # TemplateSyntaxError: Unexpected end of template (expected {% end %})
    # File "<string>", line 1

Error messages include:

  • Error description
  • File name (or<string>for from_string)
  • Line number
  • Relevant source snippet

Undefined Variables

By default, undefined variables raiseUndefinedError:

from kida import UndefinedError

template = env.from_string("{{ missing }}")
try:
    html = template.render()
except UndefinedError as e:
    print(e)
    # UndefinedError: Undefined variable 'missing' in <string>:1

Handle Missing Values

Use thedefaultfilter:

{{ user.nickname | default("Anonymous") }}
{{ config.timeout | default(30) }}

Or check with is defined:

{% if user is defined %}
    {{ user.name }}
{% end %}

Template Not Found

from kida import TemplateNotFoundError

try:
    template = env.get_template("nonexistent.html")
except TemplateNotFoundError as e:
    print(e)
    # TemplateNotFoundError: Template 'nonexistent.html' not found in: templates/

Runtime Errors

Python errors during rendering include template context:

template = env.from_string("{{ items[10] }}")
try:
    html = template.render(items=[1, 2, 3])
except IndexError as e:
    # Traceback shows template location
    pass

Debug Filter

Inspect values during development:

{{ posts | debug }}
{{ posts | debug("my posts") }}

Output (to stderr):

DEBUG [my posts]: <list[5]>
  [0] Post(title='First Post', weight=10)
  [1] Post(title='Second', weight=None)  <-- None: weight
  ...

Component Call Stack

When an error occurs inside a{% def %} component, TemplateRuntimeError and UndefinedError include a component_stackshowing the call chain that led to the error:

try:
    template.render(data=data)
except TemplateRuntimeError as e:
    for def_name, lineno, tpl_name in e.component_stack:
        print(f"  in {def_name}() at {tpl_name}:{lineno}")

The format_compact()output automatically includes the component stack when present.

Compile-Time Warnings

Kida emits Python warnings during template compilation for common pitfalls:

Warning Code Trigger
PrecedenceWarning K-WARN-001 x ?? [] | length — filter pipe binds tighter than ??
CoercionWarning "abc" | float — silent type coercion to 0.0
MigrationWarning K-WARN-002 {% set %} inside blocks when jinja2_compat_warnings=True

Access warnings on a compiled template:

template = env.get_template("page.html")
for w in template.warnings:
    print(f"{w.code}: {w.message} (line {w.lineno})")

These are standard Python warnings — filter them with warnings.filterwarnings("ignore", category=PrecedenceWarning).

Error Codes

Every Kida exception carries anErrorCodethat categorizes the error and links to documentation:

Code Category Description
K-LEX-001 Lexer Unterminated string literal
K-LEX-002 Lexer Invalid character in template
K-PAR-001 Parser Unexpected token
K-PAR-002 Parser Unclosed block tag
K-PAR-003 Parser Invalid expression
K-PAR-006 Parser Invalid identifier (hyphen in block/fragment name)
K-RUN-001 Runtime Undefined variable
K-RUN-002 Runtime Type error in expression
K-RUN-003 Runtime Index out of range
K-RUN-004 Runtime Filter execution error
K-TPL-001 Template Template not found
K-TPL-002 Template Template syntax error
K-TPL-003 Template Circular macro import
K-SEC-001 Security Blocked attribute access
K-SEC-002 Security Blocked type access
K-SEC-003 Security Range limit exceeded
K-SEC-004 Security Blocked callable
K-SEC-005 Security Output limit exceeded

Access the code programmatically:

from kida import UndefinedError, ErrorCode

try:
    template.render()
except UndefinedError as e:
    print(e.code)        # ErrorCode.K_RUN_001
    print(e.code.value)  # "K-RUN-001"

Compact Error Format

All Kida exceptions provide aformat_compact()method that produces a structured, human-readable summary for terminal output or logging:

from kida import TemplateError

try:
    template.render()
except TemplateError as e:
    print(e.format_compact())

Output:

K-RUN-001: Undefined variable 'usernme' in base.html:42

     |
> 42 | <h1>{{ usernme }}</h1>
     |

Hint: Did you mean 'username'?
Docs: https://lbliii.github.io/kida/docs/errors/#k-run-001

The compact format includes:

  • Error code and description
  • Source snippet with line numbers and error pointer
  • Hint with suggestions (typo corrections,defaultfilter usage)
  • Docs link to the relevant error code documentation

This is the recommended format for frameworks that wrap Kida errors (like Chirp and Bengal).

Source Snippets

For programmatic access to the source context around an error, use thesource_snippetattribute:

from kida import UndefinedError, SourceSnippet

try:
    template.render()
except UndefinedError as e:
    snippet: SourceSnippet | None = e.source_snippet
    if snippet:
        print(snippet.lines)       # List of (line_number, line_text) tuples
        print(snippet.error_line)  # The line number with the error
        print(snippet.filename)    # Template filename

You can also build snippets manually with build_source_snippet():

from kida import build_source_snippet

snippet = build_source_snippet(source_text, error_line=42, context_lines=3)

Common Error Patterns

Missing Variable

UndefinedError: Undefined variable 'usre' in page.html:5

Fix: Check for typos, ensure variable is passed to render().

Attribute Error

UndefinedError: 'dict' object has no attribute 'nmae'

Fix: Check attribute spelling, verify object type.

Type Error in Filter

TypeError: object of type 'NoneType' has no len()

Fix: Usedefaultfilter or check for None.

{{ items | default([]) | length }}

Template Not Found

TemplateNotFoundError: Template 'bsae.html' not found

Fix: Check file path, verify loader configuration.

Hyphen in Block or Fragment Name

Kida Parse Error [K-PAR-006]: Invalid block name: 'settings-status' contains a hyphen

Fix: Use underscores instead of hyphens. Change{% block settings-status %} to {% block settings_status %}.

Error Handling in Production

from kida import TemplateError

def render_page(template_name, **context):
    try:
        return env.render(template_name, **context)
    except TemplateError as e:
        # Log the compact format for clean terminal output
        logger.error("Template error:\n%s", e.format_compact())
        # Return fallback
        return env.render("error.html", error=str(e))

Best Practices

Validate Context

def render_safely(template, **context):
    # Validate required fields
    required = ["title", "user"]
    missing = [k for k in required if k not in context]
    if missing:
        raise ValueError(f"Missing context: {missing}")

    return template.render(**context)

Use Default Values

{# Defensive defaults #}
{{ user.name | default("Guest") }}
{{ items | default([]) | length }}
{{ config.get("timeout", 30) }}

Check Before Access

{% if user and user.profile %}
    {{ user.profile.bio }}
{% end %}

See Also

) — Catch rendering errors inside templates with{% try %}/{% fallback %}

  • [Undefined Variable] — Debug undefined errors
  • [Template Not Found] — Fix loading issues
  • API Reference — Exception classes