Addition of a debugging mode and include more robust exception handling for ConfigTemplate rendering to aid with template development. #11414

Open
opened 2025-12-29 21:44:57 +01:00 by adam · 4 comments
Owner

Originally created by @pobk on GitHub (Jul 25, 2025).

NetBox version

4.3

Feature type

Change to existing functionality

Proposed functionality

Introduce a per-template debug field on the ConfigTemplate model, enabling targetted debug output and safer rendering.

This proposal includes:

  1. Addition of debug boolean field to ConfigTemplate model. When enabled, rendering of Jinja2 templates are verbose and developer friendly. When disabled, renders are user-safe and production-friendly.
  2. Add a debug argument to render_jinja2() function.
    Behaviour:
    • debug == False renders the
      • Jinja2 TemplateError exceptions are caught and displayed in the UI with:
        • Exception type and message
        • Template name, if available
        • Line number, if available
      • Other unhandled Python, Django or NetBox exceptions (e.g. Attribute Error, TypeError) are also caught, but instead of detonating a NetBox unhandled exception error, a user-facing error is shown, inline.
    • debug == True:
      • Full exception tracebacks are rendered in the UI.
      • Jinja2's debug extension is enabled, so template authors can dig into their templating more
      • NetBox makes no effort to protect feelings.
  3. When templates use {% extends %} and {% includes %}, error context should include, if available, the correct file and line, rather than blaming the top-level template like it's the only one that showed up to work.
  4. Optional: Evaluate integrating or writing an implementation of the jinja-try-catch extension to give template authors some defensive tools.

This should not require global DEBUG = True mode and should only affect the template where debug is explicitly enabled.

Use case

  1. The hidden syntax bomb

A user builds a template with an include:

{% extends "core_device.j2" %}
{% block content %}
Device: {{ device.name }}
{% include "routing.j2" %}
{% endblock %}

Inside routing.j2, someone left this timebomb:

router ospf 
  network {{ ospf_network  // no closing braces, just vibes

NetBox currently prints the message without adding context information or any kind of hint that it was buried inside routing.j2.

With debug == True, the error should detail template and line number.

jinja2.exceptions.TemplateSyntaxError: unexpected end of template in routing.j2 (line 2)

  1. The ManyRelatedManager Meltdown

Thte template author assumes they can iterate over interfaces:

{% for iface in device.interfaces %}
interface {{ iface.name }}
{% endfor %}

But device.interfaces is a Django ManyRelatedManager. So NetBox raises an unhandled exception:

'ManyRelatedManager' object not iterable

Which, if you're lucky, results in a wall of red. If you're unlucky, nothing renders and you just think your device has no interfaces.

In wrapping the the rendering within exception handling in render_jinja2(), this output would be:

TypeError: 'ManyRelatedManager' object is not iterable
Template: base_config.j2
Line: 3
Hint: Did you mean 'device.interfaces.all()'?

Caveat: I'm unsure what information is available in the TypeError exception from within a rendered template. Also, I'm probably hallucinating the actual exception messages here. These have been part of my therapy sessions for the last few weeks and my therapist says I'm doing much better now.

Database changes

ConfigTemplate will require the addition of:

debug field of type boolean default False.

External dependencies

Optional: jinja-try-catch

  • If integrated, template authors could suppress runtime errors directly inside the template logic.
Originally created by @pobk on GitHub (Jul 25, 2025). ### NetBox version 4.3 ### Feature type Change to existing functionality ### Proposed functionality Introduce a per-template `debug` field on the ConfigTemplate model, enabling targetted debug output and safer rendering. This proposal includes: 1. Addition of `debug` boolean field to ConfigTemplate model. When enabled, rendering of Jinja2 templates are verbose and developer friendly. When disabled, renders are user-safe and production-friendly. 2. Add a `debug` argument to `render_jinja2()` function. *Behaviour*: * `debug == False` renders the * Jinja2 `TemplateError` exceptions are caught and displayed in the UI with: * Exception type and message * Template name, if available * Line number, if available * Other unhandled Python, Django or NetBox exceptions (e.g. Attribute Error, TypeError) are also caught, but instead of detonating a NetBox unhandled exception error, a user-facing error is shown, inline. * `debug == True`: * Full exception tracebacks are rendered in the UI. * Jinja2's debug extension is enabled, so template authors can dig into their templating more * NetBox makes no effort to protect feelings. 3. When templates use `{% extends %}` and `{% includes %}`, error context should include, if available, the correct file and line, rather than blaming the top-level template like it's the only one that showed up to work. 4. Optional: Evaluate integrating or writing an implementation of the jinja-try-catch extension to give template authors some defensive tools. This should not require global `DEBUG = True` mode and should only affect the template where `debug` is explicitly enabled. ### Use case 1. The hidden syntax bomb A user builds a template with an include: ``` {% extends "core_device.j2" %} {% block content %} Device: {{ device.name }} {% include "routing.j2" %} {% endblock %} ``` Inside `routing.j2`, someone left this timebomb: ``` router ospf network {{ ospf_network // no closing braces, just vibes ``` NetBox currently prints the message without adding context information or any kind of hint that it was buried inside `routing.j2`. With `debug == True`, the error should detail template and line number. > `jinja2.exceptions.TemplateSyntaxError: unexpected end of template in routing.j2 (line 2)` 2. The ManyRelatedManager Meltdown Thte template author assumes they can iterate over interfaces: ``` {% for iface in device.interfaces %} interface {{ iface.name }} {% endfor %} ``` But `device.interfaces` is a Django `ManyRelatedManager`. So NetBox raises an unhandled exception: > `'ManyRelatedManager' object not iterable` Which, if you're lucky, results in a wall of red. If you're unlucky, nothing renders and you just think your device has no interfaces. In wrapping the the rendering within exception handling in `render_jinja2()`, this output would be: ``` TypeError: 'ManyRelatedManager' object is not iterable Template: base_config.j2 Line: 3 Hint: Did you mean 'device.interfaces.all()'? ``` Caveat: I'm unsure what information is available in the TypeError exception from within a rendered template. Also, I'm probably hallucinating the actual exception messages here. These have been part of my therapy sessions for the last few weeks and my therapist says I'm doing much better now. ### Database changes ConfigTemplate will require the addition of: `debug` field of type boolean default False. ### External dependencies Optional: [jinja-try-catch](https://github.com/RewstApp/jinja-try-catch) * If integrated, template authors could suppress runtime errors directly inside the template logic.
adam added the type: featurestatus: needs ownercomplexity: mediumnetbox labels 2025-12-29 21:44:57 +01:00
Author
Owner

@jnovinger commented on GitHub (Jul 25, 2025):

@pobk , did you mean https://github.com/RewstApp/jinja-try-catch?

@jnovinger commented on GitHub (Jul 25, 2025): @pobk , did you mean https://github.com/RewstApp/jinja-try-catch?
Author
Owner

@pobk commented on GitHub (Jul 28, 2025):

@pobk , did you mean https://github.com/RewstApp/jinja-try-catch?

I did. Forgive me, for I am (occasionally) a high-functioning idiot.

@pobk commented on GitHub (Jul 28, 2025): > [@pobk](https://github.com/pobk) , did you mean https://github.com/RewstApp/jinja-try-catch? I did. Forgive me, for I am (occasionally) a high-functioning idiot.
Author
Owner

@arthanson commented on GitHub (Jul 31, 2025):

This would make more sense as a configuration parameter, would need some more detail on the actual implementation.

@arthanson commented on GitHub (Jul 31, 2025): This would make more sense as a configuration parameter, would need some more detail on the actual implementation.
Author
Owner

@pobk commented on GitHub (Aug 6, 2025):

This would make more sense as a configuration parameter, would need some more detail on the actual implementation.

Hi @arthanson,

What kind of detail would you need here? I'm happy to do some digging into this if necessary... I've already spent some time working through the netbox codebase to try and get my head around why I was getting exceptions.

On the subject of configuration parameter... I'm assuming here you're talking about adding this to the dcim.ConfigRevision model?

Just my 2p, I don't think a configuration parameter for this would be best here.

A configuration flag in the files would necessitate restartingt a netbox instance and would then apply globally unless you fudge some kind of filtering. Furthermore, in a large team where different engineers are responsible for different areas of the network, enabling debugging globally might not be prudent.

I think this really needs to be flag on the configuration template object so that it can be enabled locally on a specific template.

My specific use case: If I'm I'm making changes to a production template, I will create a new git branch, a new data-source for that branch and then work independently so that my changes aren't having adverse affects on deployments that others in my team are working on. This means that I have a separate config template object which syncs to the development branch...

If the flag were global, then I would likely impact production deployments.

~ P

@pobk commented on GitHub (Aug 6, 2025): > This would make more sense as a configuration parameter, would need some more detail on the actual implementation. Hi @arthanson, What kind of detail would you need here? I'm happy to do some digging into this if necessary... I've already spent some time working through the netbox codebase to try and get my head around why I was getting exceptions. On the subject of configuration parameter... I'm assuming here you're talking about adding this to the `dcim.ConfigRevision` model? Just my 2p, I don't think a configuration parameter for this would be best here. A configuration flag in the files would necessitate restartingt a netbox instance and would then apply globally unless you fudge some kind of filtering. Furthermore, in a large team where different engineers are responsible for different areas of the network, enabling debugging globally might not be prudent. I think this really needs to be flag on the configuration template object so that it can be enabled locally on a specific template. My specific use case: If I'm I'm making changes to a production template, I will create a new git branch, a new data-source for that branch and then work independently so that my changes aren't having adverse affects on deployments that others in my team are working on. This means that I have a separate config template object which syncs to the development branch... If the flag were global, then I would likely impact production deployments. ~ P
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#11414