mjmlio/mjml · warning

e.formattedMessage (compiled MJML errors printed via console

Error message

e.formattedMessage (compiled MJML errors printed via console.warn)

What it means

Not a thrown error but a diagnostic output path: in watchFiles' readAndCompile, when mjml2html returns compiled.errors, each error's formattedMessage is printed via console.warn so watch-mode users see validation/parse warnings with file/line context while compilation continues. The file is still written if outputToFile succeeds; warnings indicate issues without aborting the watch loop.

Source

Thrown at packages/mjml-cli/src/commands/watchFiles.js:62

    watcher.add(difference(files.toWatch, files.watched))
    watcher.unwatch(difference(files.watched, files.toWatch))
    /* eslint-enable no-use-before-define */
  }
  const readAndCompile = async (file) => {
    const { config } = options
    const beautify = config.beautify && config.beautify !== 'false'
    const minify = config.minify && config.minify !== 'false'
    const content = readFile(file).mjml
    const compiled = await mjml2html(content, {
      ...config,
      beautify,
      minify,
      filePath: file,
      actualPath: file,
    })

    compiled.errors.forEach((e) => console.warn(e.formattedMessage))

    try {
      await outputToFile({ file, compiled })
      console.log(`${file} - Successfully compiled`)
    } catch (e) {
      console.log(`${file} - Error while compiling file`)
    }
  }

  const watcher = chokidar
    .watch(input.map((i) => i.replace(/\\/g, '/')))
    .on('change', (file) => synchronyzeWatcher(path.resolve(file)))
    .on('add', (file) => {
      const filePath = path.resolve(file)
      console.log(`Now watching file: ${filePath}`)

      const matchInputOption = input.reduce(
        (found, file) =>

View on GitHub (pinned to 6c01d35af5)

Solutions

  1. Read each printed formattedMessage; fix the reported file/line so warnings disappear on the next save.
  2. Ignore transient warnings during rapid successive saves; re-save once editing is complete.
  3. Harden the watch pipeline by validating files (mjml --validate or validationLevel checks) before output.
  4. If warnings should block output, wrap compilation and check compiled.errors before calling outputToFile.

Example fix

// before: compile and always output
compiled.errors.forEach((e) => console.warn(e.formattedMessage))
await outputToFile({ file, compiled })
// after: optionally skip output on errors
if (compiled.errors.length) {
  compiled.errors.forEach((e) => console.warn(e.formattedMessage))
  return // skip writing broken output
}
await outputToFile({ file, compiled })
Defensive patterns

Strategy: try-catch

Validate before calling

const fs = require('fs');
if (!fs.existsSync(file)) { console.error(`Watched file vanished: ${file}`); return; }
if (!fs.readFileSync(file, 'utf8').trim()) { console.warn(`Skipped empty file: ${file}`); return; }

Try / catch

const compiled = mjml2html(source, { beautify, minify, filePath: file })
if (compiled.errors.length) {
  compiled.errors.forEach((e) => console.warn(e.formattedMessage))
  // decide: continue writing (warn-only) or return to block output
}
try {
  await outputToFile({ file, compiled })
} catch (e) {
  console.error(`${file} - output failed:`, e.message)
}

Prevention

When it happens

Trigger: Running `mjml --watch` on a directory while a watched file contains validation errors (unknown components, bad attributes) or recoverable parse issues — mjml2html returns an errors array which readAndCompile prints line by line.

Common situations: Live-editing templates in an editor and saving intermediate broken states; auto-formatters introducing invalid attributes; partial files being written while the watcher compiles them mid-save.

Related errors


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