mjmlio/mjml · warning

[MJML] Some mj-include paths were denied because they are…

Error message

[MJML] Some mj-include paths were denied because they are outside the allowed directories.\n  Files: ${filesWithDeniedIncludes.join(', ')}\n  Add the include directories to --config.includePath to allow them.\n  See https://documentation.mjml.io/#mj-include for security notes.

What it means

MJML's CLI scans the compiled HTML output of each input file for the sentinel comment '<!-- mj-include denied -->'. If found, it means some mj-include tags resolved to paths outside the directories allowed by includePath, and mjml-core replaced them with this denial marker instead of the included content. The CLI warns so the output is not silently missing included fragments.

Solutions

  1. Add the directory containing the included files to the CLI config, e.g. mjml --config.includePath ./src/templates ./shared/templates input.mjml
  2. Change the mj-include paths so they resolve inside an allowed directory (relative paths within the project)
  3. Verify which files are affected via the 'Files:' list and inspect their compiled output for the denied marker
  4. If the includes are intentional and safe, explicitly enumerate only the needed folders rather than widening to a parent/root directory

Example fix

// before
mjml templates/email.mjml
// after
mjml templates/email.mjml --config.includePath ./templates ./shared/partials
Defensive patterns

Strategy: validation

Validate before calling

const DENIED_RE = /<!--\s*mj-include denied\s*-->/i
const denied = files.filter(f => DENIED_RE.test(fs.readFileSync(f, 'utf8')))
if (denied.length) throw new Error(`Denied mj-include in: ${denied.join(', ')}`)

Type guard

const hasDeniedIncludes = (html) => typeof html === 'string' && /<!--\s*mj-include denied\s*-->/i.test(html)

Prevention

When it happens

Trigger: Running the mjml CLI (packages/mjml-cli) on files containing <mj-include path="..." /> where the resolved path is outside the configured --config.includePath directories (or includePath is unset). The warning fires only when at least one compiled output matches DENIED_RE.

Common situations: CI builds after moving template files to a new directory; using absolute or ../ paths in mj-include; sharing templates across repos without extending includePath; upgrading MJML where includePath restrictions became stricter.

Related errors


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

Appendix: source

Thrown at packages/mjml-cli/src/client.js:333

      failedStream.push({ file: i.file, error: e })
    }
  }

  convertedStream.forEach((s) => {
    if (get(s, 'compiled.errors.length')) {
      console.error(map(s.compiled.errors, 'formattedMessage').join('\n')) // eslint-disable-line no-console
    }
  })

  // Warn when includes were enabled but some include paths were denied (outside allowed roots).
  if (config.ignoreIncludes === false) {
    const DENIED_RE = /<!--\s*mj-include denied\s*-->/i
    const filesWithDeniedIncludes = convertedStream
      .filter((s) => s.compiled && s.compiled.html && DENIED_RE.test(s.compiled.html))
      .map((s) => s.file)
    if (filesWithDeniedIncludes.length) {
      // eslint-disable-next-line no-console
      console.warn(
        `[MJML] Some mj-include paths were denied because they are outside the allowed directories.\n` +
          `  Files: ${filesWithDeniedIncludes.join(', ')}\n` +
          `  Add the include directories to --config.includePath to allow them.\n` +
          `  See https://documentation.mjml.io/#mj-include for security notes.`,
      )
    }
  }

  // Warn when mj-include tags are present but includes are still ignored.
  if (config.ignoreIncludes !== false) {
    const MJ_INCLUDE_RE = /<mj-include\b/i
    const filesWithIgnoredIncludes = inputs
      .filter((i) => i && i.mjml && MJ_INCLUDE_RE.test(i.mjml))
      .map((i) => i.file)
    if (filesWithIgnoredIncludes.length) {
      // eslint-disable-next-line no-console
      console.warn(
        `[MJML] mj-include tags were found but includes are disabled by default.\n` +

View on GitHub (pinned to 6c01d35af5)