# Error Boundaries URL: /docs/usage/error-boundaries/ Section: usage Tags: usage, error-handling, resilience -------------------------------------------------------------------------------- Error Boundaries {% try %}...{% fallback %}...{% end %} catches rendering errors inside a template and renders fallback content instead of crashing the page. Basic usage {% try %} {{ user.profile.avatar_url }} {% fallback %} <img src="/default-avatar.png"> {% end %} If user.profile.avatar_url raises an UndefinedError, the fallback renders. If no error occurs, the body renders normally and the fallback is ignored. What gets caught Error boundaries catch these exception types: Exception Example UndefinedError {{ missing_var }} TemplateRuntimeError Errors from includes, macros, filters TypeError {{ 42 \| join(",") }} (filter type mismatch) ValueError Value conversion failures Syntax errors are not caught -- they are raised at parse time, before rendering begins. Partial output is discarded When the body errors partway through, any output already produced by that body is thrown away. Only the fallback renders: {% try %} <p>Hello, {{ user.name }}!</p> <p>Your role: {{ user.role }}</p> {# errors here #} {% fallback %} <p>Could not load user info.</p> {% end %} Even though Hello, Alice! was rendered before the error, the entire body is discarded and only Could not load user info. appears. Accessing the error Add a name after {% fallback %} to bind the caught error as a dict: {% try %} {{ widget.render() }} {% fallback err %} <div class="error"> <p>Widget failed: {{ err.message }}</p> <small>{{ err.type }}</small> </div> {% end %} The error dict has these fields: Field Type Description message str The exception message type str Exception class name (e.g. "UndefinedError") template str | None Template name where the error occurred line int | None Line number of the error Nesting Error boundaries nest. The innermost {% try %} catches first: {% try %} {% try %} {{ risky_operation() }} {% fallback %} <p>Inner fallback</p> {% end %} {% fallback %} <p>Outer fallback (only if inner also fails)</p> {% end %} If the inner fallback itself errors, the outer boundary catches it. Inside loops Use {% try %} inside a loop to handle errors per-iteration without breaking the whole list: <ul> {% for item in items %} <li> {% try %} {{ render_item(item) }} {% fallback %} <span class="error">Failed to render item</span> {% end %} </li> {% end %} </ul> One bad item doesn't take down the rest of the list. With components Error boundaries catch errors from any template code -- includes, macros, and component calls: {# Catch errors from an included partial #} {% try %} {% include "widgets/dashboard.html" %} {% fallback %} <p>Dashboard unavailable.</p> {% end %} {# Catch errors from a component #} {% try %} {{ chart(data=analytics_data) }} {% fallback err %} <p>Chart error: {{ err.message }}</p> {% end %} Streaming Error boundaries work correctly in streaming mode (render_stream()). The try body is buffered internally -- chunks are only yielded to the stream after the body completes without error. If the body errors, the buffer is discarded and the fallback streams normally. for chunk in template.render_stream(): response.write(chunk) No partial try-body output leaks into the stream. Patterns Graceful degradation Wrap optional sections so the page still renders when a data source fails: <main> <h1>{{ page.title }}</h1> {{ page.content }} {% try %} {% include "partials/recommendations.html" %} {% fallback %} {# Recommendations unavailable -- page still works #} {% end %} </main> Development vs. production Show error details in development, hide them in production: {% try %} {{ complex_widget() }} {% fallback err %} {% if debug %} <pre>{{ err.type }}: {{ err.message }}</pre> {% else %} <p>Something went wrong.</p> {% end %} {% end %} Per-item resilience in data-driven pages {% for post in posts %} {% try %} <article> <h2>{{ post.title }}</h2> <p>{{ post.excerpt }}</p> <span>By {{ post.author.name }}</span> </article> {% fallback %} <article class="error"> <h2>Post unavailable</h2> </article> {% end %} {% end %} -------------------------------------------------------------------------------- Metadata: - Word Count: 624 - Reading Time: 3 minutes