Module code appears raw in the email

I tried to code the following module:

It works very well when making changes in the Email Marketing interface for sending, but it throws this bug when adjusting email templates, and I don’t know how to fix it.

Hey @ThomC,

Welcome back!! :waving_hand:

The first thing I’d check is if your code was pasted into a Rich Text field or added as template text instead of being rendered as a custom module. Here are a few resources I found for you that may be helpful while we wait for others in the community to chime in:

Tagg in some folks from the community to see if they have any tips and tricks on this!

Hey @GRajput, @Alex_Boissonneault, @danielsinewe – do you have any suggestions for @ThomC on this?

Thank you!!

Sam, Community Manager

Thanks for looping me in, @stassey.

Hey @ThomC, good to see you here!
Regarding the ask, this is the raw code dump is a classic symptom: when HubSpot shows your HubL as plain text instead of running it, it almost always means the template engine hit an error and “bailed out,” so it prints the source instead of the rendered output. The reason it behaves differently between the two editors is context, in the marketing email editor your module’s fields are fully populated with their defaults, but in the email template editor those field/context values (like layout_group, gutter_a, gutter_b, image_src, inner_width, final_image_width) can come through empty, and the moment your arithmetic runs on an undefined value, e.g. (10 + gutter_a) or the img_px calculations, the whole module fails and falls back to raw.

A few things I’d try, in order:

  1. Isolate it first. Temporarily replace the module body with something trivial like {{ "test" }} and drop it into the template. If that renders, the module wiring is fine (so it’s not a rich-text-vs-module issue) and the problem is inside the logic, which is what I’d bet on here.
  2. Default every numeric variable. Any value you do math on should have a fallback, e.g. {% set gutter_a = gutter_a|default(0) %}, same for gutter_b, inner_width, final_image_width, image_col_px, etc. Null-safe defaults on the numbers are usually what fixes the “works in one editor, breaks in the other” behaviour.
  3. Add logic back in chunks. Once the defaults are in, reintroduce your {% set %} blocks and the layout {% if/elif/else %} a piece at a time — the exact point it flips back to raw tells you which line the engine is choking on. With this many nested conditionals, a single mismatched {% if %} / {% endif %} will also trigger the same raw output.

These docs are handy for the module/template context side of it:

If you can share the full module code (or the fields.json), I’m happy to point to the exact line that’s breaking. My money’s on an unguarded numeric variable. :slight_smile:

Hello @Alex_Boissonneault
Thank you very much for your feedback and for your help in advance.

Here is the JSON in question for this module, hopefully it answers your question :slight_smile:


{
“children”:
{
“display_width”: null,
“id”: “f9973df8-97b1-f26a-9892-8dc40d337328”,
“label”: “Title”,
“locked”: false,
“name”: “title”,
“required”: false,
“type”: “richtext”
},
{
“display_width”: null,
“id”: “785b2db9-5356-239b-0b75-69e4178ef0d4”,
“label”: “Content”,
“locked”: false,
“name”: “body_text”,
“required”: false,
“type”: “richtext”
},
{
“default”: false,
“display”: “checkbox”,
“display_width”: null,
“id”: “5fa37d14-3a07-da1e-f8dd-4746b19cf397”,
“label”: “Show separator”,
“locked”: false,
“name”: “show_separator”,
“required”: false,
“type”: “boolean”
},
{
“allow_new_line”: false,
“default”: “”,
“display_width”: null,
“id”: “6b8518dd-fc4d-1e08-abf3-3b184803217c”,
“label”: “Training duration”,
“locked”: false,
“name”: “duration”,
“required”: false,
“type”: “text”
},
{
“children”:
{
“default”: false,
“display”: “checkbox”,
“display_width”: null,
“id”: “64b1ae4f-6691-fc2f-9395-155cccce62bf”,
“label”: “Show rating”,
“locked”: false,
“name”: “show_rating”,
“required”: false,
“type”: “boolean”
},
{
“default”: 5,
“display”: “text”,
“display_width”: null,
“id”: “cae0461b-3220-1a9b-9ded-ae052c4593ab”,
“label”: “Star rating”,
“locked”: false,
“max”: 5,
“min”: 0,
“name”: “rating”,
“required”: false,
“step”: 0.1,
“suffix”: “/5”,
“type”: “number”
},
{
“default”: false,
“display”: “checkbox”,
“display_width”: null,
“id”: “327798eb-8588-1045-3f60-5ee5e3a0e70e”,
“label”: “Show second separator”,
“locked”: false,
“name”: “show_separator_2”,
“required”: false,
“type”: “boolean”
}

,
“default”: {
“show_rating”: false,
“rating”: 5,
“show_separator_2”: false
},
“display_width”: null,
“expanded”: false,
“group_occurrence_meta”: null,
“id”: “f6a2fc1b-db9b-6612-aec4-5f566c79487f”,
“label”: “rating settings”,
“locked”: false,
“name”: “rating_settings”,
“required”: false,
“tab”: “CONTENT”,
“type”: “group”
}

,
“default”: {
“show_separator”: false,
“duration”: “”,
“rating_settings”: {
“show_rating”: false,
“rating”: 5,
“show_separator_2”: false
}
},
“display_width”: null,
“expanded”: false,
“group_occurrence_meta”: null,
“id”: “dc0f3b82-14bf-52ac-0ad1-0f9bd02860fc”,
“label”: “Content management”,
“locked”: false,
“name”: “content”,
“required”: false,
“tab”: “CONTENT”,
“type”: “group”
},
{
“children”:
{
“default”: {
“src”: “”,
“alt”: null,
“loading”: “lazy”
},
“display_width”: null,
“id”: “fe8788ee-a211-3bd7-ae9f-35664f8bdd41”,
“label”: “Image”,
“locked”: false,
“name”: “image”,
“required”: false,
“resizable”: false,
“responsive”: true,
“show_loading”: false,
“type”: “image”
},
{
“display”: “slider”,
“display_width”: null,
“id”: “49833378-f559-42af-bec5-3fda23c66c07”,
“label”: “Image size (%)”,
“locked”: false,
“max”: 100,
“min”: 1,
“name”: “image_width”,
“required”: false,
“step”: 1,
“type”: “number”
},
{
“default”: 12,
“display”: “slider”,
“display_width”: null,
“id”: “a447de7a-f854-712e-1d71-46b274e9c111”,
“label”: “Image corner radius (px)”,
“locked”: false,
“name”: “image_border_radius”,
“required”: false,
“step”: 1,
“type”: “number”
},
{
“choices”:

“left”,
“Left”

,

“center”,
“Center”

,

“right”,
“Right”


,
“default”: “center”,
“display”: “select”,
“display_width”: null,
“id”: “8486294c-8118-f661-b603-bda37703a803”,
“label”: “Image placement”,
“locked”: false,
“multiple”: false,
“name”: “image_align”,
“preset”: null,
“reordering_enabled”: true,
“required”: false,
“type”: “choice”
}

,
“default”: {
“image”: {
“src”: “”,
“alt”: null,
“loading”: “lazy”
},
“image_border_radius”: 12,
“image_align”: “center”
},
“display_width”: null,
“expanded”: false,
“group_occurrence_meta”: null,
“id”: “17732df3-bc52-571f-8935-2e020dc2a357”,
“label”: “Icon / image management”,
“locked”: false,
“name”: “media”,
“required”: false,
“tab”: “CONTENT”,
“type”: “group”
},
{
“children”:
{
“choices”:

“1-2”,
“1/2”

,

“1-3_2-3”,
“1/3 - 2/3”

,

“2-3_1-3”,
“2/3 - 1/3”

,

“1-4_3-4”,
“1/4 - 3/4”

,

“3-4_1-4”,
“3/4 - 1/4”

,

“1-5_4-5”,
“1-5 / 4-5”

,

“4-5_1-5”,
“4-5 / 1-5”

,

“1-6_5-6”,
“1-6 / 5-6”

,

“5-6_1-6”,
“5-6 / 1-6”


,
“default”: “1-2”,
“display”: “select”,
“display_width”: null,
“id”: “da603637-94a8-ab66-55c3-6a2b0829a5ad”,
“label”: “Column choice”,
“locked”: false,
“multiple”: false,
“name”: “column_ratio”,
“preset”: null,
“reordering_enabled”: true,
“required”: false,
“type”: “choice”
},
{
“choices”:

“image_left”,
“Image on the left”

,

“image_right”,
“Image on the right”


,
“default”: “image_left”,
“display”: “select”,
“display_width”: null,
“id”: “713564f5-8527-45e6-79ee-6ef76b85018c”,
“label”: “Column inversion”,
“locked”: false,
“multiple”: false,
“name”: “column_order”,
“preset”: null,
“reordering_enabled”: true,
“required”: false,
“type”: “choice”
},
{
“default”: 0,
“display”: “slider”,
“display_width”: null,
“id”: “ca9ddb2f-25e3-b30c-1e1e-bbff93959e60”,
“label”: “Spacing between columns (px)”,
“locked”: false,
“name”: “gutter_width”,
“required”: false,
“step”: 1,
“type”: “number”
},
{
“choices”:

“side_by_side”,
“Side by side on desktop & mobile”

,

“stack”,
“Stacked only on mobile”

,

“stack_always”,
“Stacked on desktop & mobile”


,
“default”: “side_by_side”,
“display”: “radio”,
“display_width”: null,
“id”: “76c109a1-9c37-343d-081d-d4db7847ab1b”,
“label”: “Column layout”,
“locked”: false,
“multiple”: false,
“name”: “mobile_layout”,
“preset”: null,
“reordering_enabled”: true,
“required”: false,
“type”: “choice”
}

,
“default”: {
“column_ratio”: “1-2”,
“column_order”: “image_left”,
“gutter_width”: 0,
“mobile_layout”: “side_by_side”
},
“display_width”: null,
“expanded”: false,
“group_occurrence_meta”: null,
“id”: “770f75cb-c501-1a87-f0db-e4dd46c87878”,
“label”: “Column and spacing management”,
“locked”: false,
“name”: “layout”,
“required”: false,
“tab”: “CONTENT”,
“type”: “group”
},
{
“children”:
{
“allow_new_line”: false,
“default”: “Sign up for the training”,
“display_width”: null,
“id”: “88957bd8-0ff0-98dc-3d01-9f693060a02a”,
“label”: “Button text”,
“locked”: false,
“name”: “cta_label”,
“required”: false,
“type”: “text”
},
{
“default”: {
“url”: {
“content_id”: null,
“type”: “EXTERNAL”,
“href”: “”
},
“open_in_new_tab”: false,
“no_follow”: false
},
“display_width”: null,
“id”: “5628c198-015b-3e19-98ca-74a2019df2b8”,
“label”: “Button link”,
“locked”: false,
“name”: “link”,
“required”: false,
“show_advanced_rel_options”: false,
“supported_types”:
“EXTERNAL”

,
“type”: “link”
},
{
“default”: {
“color”: null,
“opacity”: 100
},
“display_width”: null,
“id”: “6ec9822c-fcb5-0f24-69e9-8a27f1a4b0dc”,
“label”: “CTA color”,
“locked”: false,
“name”: “cta_bg_color”,
“required”: false,
“show_opacity”: false,
“type”: “color”
}

,
“default”: {
“cta_label”: “Sign up for the training”,
“link”: {
“url”: {
“content_id”: null,
“type”: “EXTERNAL”,
“href”: “”
},
“open_in_new_tab”: false,
“no_follow”: false
},
“cta_bg_color”: {
“color”: null,
“opacity”: 100
}
},
“display_width”: null,
“expanded”: false,
“group_occurrence_meta”: null,
“id”: “fe1fb3c3-73fa-9744-dcb2-4fac77bc1700”,
“label”: “CTA management”,
“locked”: false,
“name”: “cta”,
“required”: false,
“tab”: “CONTENT”,
“type”: “group”
},
{
“children”:
{
“default”: {
“color”: null,
“opacity”: 100
},
“display_width”: null,
“id”: “9bebb2f1-dabe-d0ea-3937-7c72a0ce1bf1”,
“label”: “Card color”,
“locked”: false,
“name”: “card_bg_color”,
“required”: false,
“show_opacity”: false,
“type”: “color”
},
{
“default”: true,
“display”: “checkbox”,
“display_width”: null,
“id”: “b0f31c0c-9d5f-aa3a-8fb6-6a5805d11394”,
“label”: “Show separator (after the title)”,
“locked”: true,
“name”: “show_separator”,
“required”: false,
“type”: “boolean”
},
{
“default”: true,
“display”: “checkbox”,
“display_width”: null,
“id”: “2afc4a93-fe49-c1d0-d4f5-e26a0009f7b7”,
“label”: “Show separator (after the rating)”,
“locked”: true,
“name”: “show_separator_2”,
“required”: false,
“type”: “boolean”
},
{
“children”:
{
“default”: false,
“display”: “checkbox”,
“display_width”: null,
“id”: “9470f27d-a600-75c7-5761-d33ffa9561fe”,
“label”: “Show a border”,
“locked”: false,
“name”: “enabled”,
“required”: false,
“type”: “boolean”
},
{
“default”: {
“color”: null,
“opacity”: null
},
“display_width”: null,
“id”: “4af1ea42-34e0-810f-528d-430ea1cf679f”,
“label”: “Border color”,
“locked”: false,
“name”: “color”,
“required”: false,
“type”: “color”
},
{
“display”: “slider”,
“display_width”: null,
“id”: “17f2e127-9445-a760-41b7-66b496fc0425”,
“label”: “Border size”,
“locked”: false,
“name”: “border_width”,
“required”: false,
“step”: 1,
“type”: “number”
},
{
“display”: “slider”,
“display_width”: null,
“id”: “2c586ee4-d82e-d225-c0a6-430ac499a154”,
“label”: “Border radius”,
“locked”: false,
“name”: “radius”,
“required”: false,
“step”: 1,
“type”: “number”
}

,
“default”: {
“enabled”: false,
“color”: {
“color”: null,
“opacity”: null
}
},
“display_width”: null,
“expanded”: false,
“group_occurrence_meta”: null,
“id”: “deda8b64-db7f-b888-a8c0-2e92ea8cc0c4”,
“label”: “Border”,
“locked”: false,
“name”: “border”,
“required”: false,
“tab”: “CONTENT”,
“type”: “group”
},
{
“children”:
{
“default”: false,
“display”: “checkbox”,
“display_width”: null,
“id”: “389bca56-6ed7-21fe-e088-aa079e85c28c”,
“label”: “Show a shadow”,
“locked”: false,
“name”: “enabled”,
“required”: false,
“type”: “boolean”
},
{
“default”: {
“color”: null,
“opacity”: null
},
“display_width”: null,
“id”: “ab68e383-cd86-1a81-d409-63476fdf6440”,
“label”: “Shadow color”,
“locked”: false,
“name”: “color”,
“required”: false,
“type”: “color”
},
{
“display”: “slider”,
“display_width”: null,
“id”: “9856f97d-0a04-fe36-273e-8dc7b23f88f9”,
“label”: “Shadow blur size”,
“locked”: false,
“name”: “blur”,
“required”: false,
“step”: 1,
“type”: “number”
}

,
“default”: {
“enabled”: false,
“color”: {
“color”: null,
“opacity”: null
}
},
“display_width”: null,
“expanded”: false,
“group_occurrence_meta”: null,
“id”: “bec3c234-2952-ef99-5d15-472c606525bf”,
“label”: “Shadow”,
“locked”: false,
“name”: “shadow”,
“required”: false,
“tab”: “CONTENT”,
“type”: “group”
}

,
“default”: {
“card_bg_color”: {
“color”: null,
“opacity”: 100
},
“show_separator”: true,
“show_separator_2”: true,
“border”: {
“enabled”: false,
“color”: {
“color”: null,
“opacity”: null
}
},
“shadow”: {
“enabled”: false,
“color”: {
“color”: null,
“opacity”: null
}
}
},
“display_width”: null,
“expanded”: false,
“group_occurrence_meta”: null,
“id”: “f9decee0-a896-67df-e0bb-2799da6fdaeb”,
“label”: “Card style”,
“locked”: false,
“name”: “style”,
“required”: false,
“tab”: “CONTENT”,
“type”: “group”
}

}

Thanks for sending this over, @ThomC this is really helpful…

Looking at your fields.json, I think it points right at the cause. A few of your number fields have no default value set, the most important one being image_width (the “Image size (%)” slider), but also border_width, radius (border) and blur (shadow). They only have min / max / step, no default:

JSON

{
  "label": "Image size (%)",
  "name": "image_width",
  "min": 1, "max": 100, "step": 1,
  "type": "number"
  // 👈 no "default"
}

Here’s why that matters: in the marketing email editor, you’ve physically set those sliders, so the values exist and your math runs fine. But in the template editor the module renders with its defaults and where there’s no default, the value comes through as null. The moment your HubL does arithmetic on it (your final_image_width / img_px calculations feed off image_width), the expression throws, HubSpot gives up on rendering, and it dumps the raw code instead. That’s exactly the “works in one editor, raw in the other” behaviour.

Two fixes, and I’d do both:

  1. Add defaults in fields.json for every number field, e.g. "default": 100 on image_width, and sensible values on border_width, radius, blur.
  2. Null-guard the math in module.html so it can never break even if a value is empty:

hubl

{% set final_image_width = module.media.image_width|default(100) %}
{% set gutter_a = module.layout.gutter_width|default(0) %}
{% set gutter_b = module.layout.gutter_width|default(0) %}

One more thing worth checking: your calculations also use inner_width. That’s a context variable HubSpot injects when the module renders inside a real email, but it can be undefined in the template editor, so I’d give it a fallback too, e.g. {% set inner_width = inner_width|default(600) %} near the top.

Add the defaults + the |default() guards and I’d expect the raw-code issue to disappear in the template editor.

If it’s still misbehaving after that, paste your module.html and I’ll trace the exact expression, but I’m fairly confident it’s the missing image_width default.

Hi @Alex_Boissonneault,

I haven’t gotten back to you since then, but I’m currently fixing the code and have been able to identify the issue, which turned out to be related to column management, causing the code to crash.

I’m working on what’s needed to get the module up and running.

Thank you again for your help so far, as well as to @TeeTech and @stassey :slight_smile: