mjmlio/mjml · error · ValidationError

ValidationError: \n ${errors.map((e) => e.formattedMessage).

Error message

ValidationError: \n ${errors.map((e) => e.formattedMessage).join('\n')}

What it means

In strict validation mode, mjml2html runs MJMLValidator over the parsed document and, if any validation errors are found, throws a ValidationError whose message lists each error's formattedMessage. Unlike loose/skip modes which collect warnings, strict mode fails fast so invalid MJML never renders.

Source

Thrown at packages/mjml-core/src/index.js:650

    lang: get(mjml, 'attributes.lang') || 'und',
    dir: get(mjml, 'attributes.dir') || 'auto',
  }

  const validatorOptions = {
    components,
    dependencies,
    initializeType,
  }

  switch (validationLevel) {
    case 'skip':
      break

    case 'strict':
      errors = MJMLValidator(mjml, validatorOptions)

      if (errors.length > 0) {
        throw new ValidationError(
          `ValidationError: \n ${errors
            .map((e) => e.formattedMessage)
            .join('\n')}`,
          errors,
        )
      }
      break

    case 'soft':
    default:
      errors = MJMLValidator(mjml, validatorOptions)
      break
  }

  const mjBody = find(mjml.children, { tagName: 'mj-body' })
  const mjHead = find(mjml.children, { tagName: 'mj-head' })
  const mjOutsideRaws = filter(mjml.children, { tagName: 'mj-raw' })

View on GitHub (pinned to 6c01d35af5)

Solutions

  1. Fix each listed formattedMessage: correct the tag name, attribute, or structure at the given line in your MJML.
  2. Register custom components with the validator so unknown-tag errors disappear (registerMJElement/component registration).
  3. Set validationLevel to 'soft' (warn only) or 'skip' if you intentionally render non-strict documents.
  4. Compare against MJML docs for deprecated components after a version upgrade.

Example fix

// before
mjml2html(mjml, { validationLevel: 'strict' }) // <mj-foo> unknown
// after
mjml2html(mjml.replace('<mj-foo>', '<mj-text>'), { validationLevel: 'strict' })
// or: { validationLevel: 'soft' } to downgrade to warnings
Defensive patterns

Strategy: try-catch

Validate before calling

import { MJMLValidator } from 'mjml-validator'
const errs = MJMLValidator(require('mjml-parser-xml')(mjmlString))
if (errs.length) console.warn('MJML validation issues:', errs.map(e => e.formattedMessage))

Try / catch

import { ValidationError } from 'mjml-core'
try {
  return mjml2html(src, { validationLevel: 'strict' })
} catch (e) {
  if (e instanceof ValidationError) {
    console.error('MJML validation failed:', e.errors.map(x => x.formattedMessage).join('\n'))
  } else throw e
}

Prevention

When it happens

Trigger: Calling mjml2html with validationLevel: 'strict' on a document containing any validation error, e.g. unknown component tags like <mj-foo>, invalid attribute values, or components in forbidden parents.

Common situations: CI pipelines configured with strict validation that newly introduced an unsupported component or attribute typo; upgrading mjml where a component/attribute was deprecated; custom components registered under a name the validator does not know.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of mjmlio/mjml@6c01d35af5 (2026-09-02). Data as JSON: /api/errors/32a9272ff02db5b5. Report an issue: GitHub.