mjmlio/mjml · error · Error

An mj-head element add an unkown head attribute : ${attr} wi

Error message

An mj-head element add an unkown head attribute : ${attr} with params ${Array.isArray(params) ? params.join('') : params}

What it means

During mjml2html, mj-head elements register attributes into globalData (fonts, styles, attributes, etc.). If an element passes an attribute (attr) that is not in the allowed head-attribute map, mjml2html throws this Error, naming the attribute and its params, and rendering aborts with no HTML output.

Source

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

      if (Array.isArray(globalData[attr])) {
        globalData[attr].push(...params)
      } else if (Object.prototype.hasOwnProperty.call(globalData, attr)) {
        if (params.length > 1) {
          if (isObject(globalData[attr][params[0]])) {
            globalData[attr][params[0]] = {
              ...globalData[attr][params[0]],
              ...params[1],
            }
          } else {
            // eslint-disable-next-line prefer-destructuring
            globalData[attr][params[0]] = params[1]
          }
        } else {
          // 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.',
    )
  }

View on GitHub (pinned to 6c01d35af5)

Solutions

  1. Correct the head element/attribute name in the template to a supported one (mj-attributes, mj-style, mj-font, mj-breakpoint, mj-preview, mj-title)
  2. Register the custom component so its head attributes are known to mjml-core
  3. Check the attribute and params values in the message to pinpoint the offending tag
  4. Align MJML package versions (mjml-core vs custom components) so registered attributes match the template

Example fix

// before
<mj-head><mj-titel>Hi</mj-titel></mj-head>  // typo, throws
// after
<mj-head><mj-title>Hi</mj-title></mj-head>
Defensive patterns

Strategy: try-catch

Validate before calling

const ALLOWED_HEAD = new Set(['mj-attributes','mj-style','mj-font','mj-breakpoint','mj-preview','mj-title'])
for (const child of head.children) {
  if (!ALLOWED_HEAD.has(child.tagName)) throw new Error(`Unknown head element: ${child.tagName}`)
}

Type guard

const isKnownHeadAttr = (attr, known) => typeof attr === 'string' && known.has(attr)

Try / catch

try {
  const { html, errors } = mjml2html(mjml)
} catch (e) {
  if (String(e.message).startsWith('An mj-head element add an unkown head attribute')) {
    // surface the attribute name from e.message and fail fast with a clear template error
  }
  throw e
}

Prevention

When it happens

Trigger: Rendering a template containing an mj-head child (or custom component) that calls the head attribute handler with an unregistered attr — e.g. a typo like <mj-fonts>, an unknown/custom head element, or a component emitting an attribute not registered via registerComponent's head handling.

Common situations: Typos in head tags; custom components added to mj-head without registering their head attributes; version mismatches where a head attribute was renamed/removed; copying templates between MJML versions.

Related errors


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