mjmlio/mjml · error · Error
Malformed MJML. Check that your structure is correct and enc
Error message
Malformed MJML. Check that your structure is correct and enclosed in <mjml> tags.
What it means
After applying head and body helpers, mjml2html expects the processing of <mj-body> to yield a content string. If the body processing returns falsy — meaning the document did not produce a renderable body — it throws this error indicating the MJML root structure is malformed. Typically the document is missing the enclosing <mjml><mj-body> wrapper or the body failed to parse into components.
Source
Thrown at packages/mjml-core/src/index.js:809
// eslint-disable-next-line prefer-destructuring
globalData[attr] = params[0]
}
} else {
throw Error(
`An mj-head element add an unkown head attribute : ${attr} with params ${
Array.isArray(params) ? params.join('') : params
}`,
)
}
},
}
globalData.headRaw = processing(mjHead, headHelpers)
content = processing(mjBody, bodyHelpers, applyAttributes)
if (!content) {
throw new Error(
'Malformed MJML. Check that your structure is correct and enclosed in <mjml> tags.',
)
}
content = minifyOutlookConditionnals(content)
if (mjOutsideRaws.length) {
const toAddBeforeDoctype = mjOutsideRaws.filter(
(elt) =>
elt.attributes.position && elt.attributes.position === 'file-start',
)
if (toAddBeforeDoctype.length) {
globalData.beforeDoctype = toAddBeforeDoctype
.map((elt) => elt.content)
.join('\n')
}
}
View on GitHub (pinned to 6c01d35af5)
Solutions
- Ensure the document is wrapped in <mjml><mj-body>...</mj-body></mjml>.
- Verify the input file is non-empty and actually contains MJML, not plain HTML.
- Check any custom components/preprocessors for returning undefined content.
- Run the input through the MJML validator to pinpoint structural problems first.
Example fix
<!-- before -->
<mj-text>Hello</mj-text>
<!-- after -->
<mjml>
<mj-body>
<mj-text>Hello</mj-text>
</mj-body>
</mjml> Defensive patterns
Strategy: validation
Validate before calling
function isCompleteMjml(src) {
return typeof src === 'string' && /<mjml[\s>]/.test(src) && /<mj-body[\s>]/.test(src);
}
if (!isCompleteMjml(source)) throw new Error('Source is not a complete MJML document'); Try / catch
try {
const { html } = mjml2html(source)
} catch (e) {
if (e.message.startsWith('Malformed MJML')) {
console.error('Document missing <mjml>/<mj-body> wrapper or body failed to render')
} else throw e
} Prevention
- Always author full documents with <mjml><mj-body> wrappers.
- Check that source files are non-empty and are MJML, not HTML.
- Validate structure before rendering in build pipelines.
When it happens
Trigger: Calling mjml2html with input whose parsed tree has no usable <mj-body> (empty input, missing <mjml> wrapper, body that parses to nothing, or content lost to a failing preprocessor/custom component returning undefined).
Common situations: Passing an HTML file instead of MJML to mjml2html; a template whose <mjml> tag was accidentally deleted by an editor or merge; empty file/stream input in build pipelines; reading the wrong file (e.g. a partial without the root wrapper).
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- [MJML] Some mj-include paths were denied because they are ou
- [MJML] mj-include tags were found but includes are disabled
- [MJML security] includePath "${p}" is the root directory, wh
- dependency "tag" must be of type string
- "dependencies" must be an object.
AI-assisted analysis of mjmlio/mjml@6c01d35af5 (2026-09-02).
Data as JSON: /api/errors/ded5b80859f9f98a.
Report an issue: GitHub.