{"record":{"id":"ded5b80859f9f98a","repo":"mjmlio/mjml","slug":"malformed-mjml-check-that-your-structure-is-corre","errorCode":null,"errorMessage":"Malformed MJML. Check that your structure is correct and enclosed in <mjml> tags.","messagePattern":"Malformed MJML\\. Check that your structure is correct and enclosed in <mjml> tags\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mjml-core/src/index.js","lineNumber":809,"sourceCode":"          // eslint-disable-next-line prefer-destructuring\n          globalData[attr] = params[0]\n        }\n      } else {\n        throw Error(\n          `An mj-head element add an unkown head attribute : ${attr} with params ${\n            Array.isArray(params) ? params.join('') : params\n          }`,\n        )\n      }\n    },\n  }\n\n  globalData.headRaw = processing(mjHead, headHelpers)\n\n  content = processing(mjBody, bodyHelpers, applyAttributes)\n\n  if (!content) {\n    throw new Error(\n      'Malformed MJML. Check that your structure is correct and enclosed in <mjml> tags.',\n    )\n  }\n\n  content = minifyOutlookConditionnals(content)\n\n  if (mjOutsideRaws.length) {\n    const toAddBeforeDoctype = mjOutsideRaws.filter(\n      (elt) =>\n        elt.attributes.position && elt.attributes.position === 'file-start',\n    )\n    if (toAddBeforeDoctype.length) {\n      globalData.beforeDoctype = toAddBeforeDoctype\n        .map((elt) => elt.content)\n        .join('\\n')\n    }\n  }\n","sourceCodeStart":791,"sourceCodeEnd":827,"githubUrl":"https://github.com/mjmlio/mjml/blob/6c01d35af5da14108b86bbf052232e99de7ca755/packages/mjml-core/src/index.js#L791-L827","documentation":"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.","triggerScenarios":"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).","commonSituations":"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).","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."],"exampleFix":"<!-- before -->\n<mj-text>Hello</mj-text>\n<!-- after -->\n<mjml>\n  <mj-body>\n    <mj-text>Hello</mj-text>\n  </mj-body>\n</mjml>","handlingStrategy":"validation","validationCode":"function isCompleteMjml(src) {\n  return typeof src === 'string' && /<mjml[\\s>]/.test(src) && /<mj-body[\\s>]/.test(src);\n}\nif (!isCompleteMjml(source)) throw new Error('Source is not a complete MJML document');","typeGuard":null,"tryCatchPattern":"try {\n  const { html } = mjml2html(source)\n} catch (e) {\n  if (e.message.startsWith('Malformed MJML')) {\n    console.error('Document missing <mjml>/<mj-body> wrapper or body failed to render')\n  } else throw e\n}","preventionTips":["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."],"tags":["mjml","structure","parse","missing-body"],"backgroundTag":"malformed-document-structure","analyzedSha":"6c01d35af5da14108b86bbf052232e99de7ca755","analyzedAt":"2026-09-02T21:31:03.649Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}