mjmlio/mjml · warning

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

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.

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)

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.