mjmlio/mjml · error · Error
Specified filePath does not exist
Error message
Specified filePath does not exist
What it means
MJMLParser, when given a filePath option in Node, lstats it to determine the base directory used to resolve <mj-include> paths. Any lstat failure (most commonly ENOENT) makes it throw 'Specified filePath does not exist'. This protects include resolution: without a valid base directory, relative includes could silently resolve to the wrong place.
Source
Thrown at packages/mjml-parser-xml/src/index.js:54
actualPath = '.',
ignoreIncludes = true,
preprocessors = [],
includePath,
} = options
const endingTags = flow(
filter((component) => component.endingTag),
map((component) => component.getTagName()),
)({ ...components })
let cwd = process.cwd()
if (isNode && filePath) {
try {
const isDir = fs.lstatSync(filePath).isDirectory()
cwd = isDir ? filePath : path.dirname(filePath)
} catch (e) {
throw new Error('Specified filePath does not exist')
}
}
let mjml = null
let cur = null
let inInclude = !!includedIn.length
let inEndingTag = 0
const cssIncludes = []
const currentEndingTagIndexes = { startIndex: 0, endIndex: 0 }
const findTag = (tagName, tree) => find(tree.children, { tagName })
const lineIndexes = indexesForNewLine(xml)
const extraAllowedRoots = []
const addAllowedRoot = (p) => {
if (!p) return
try {
const resolved = fs.realpathSync(path.resolve(cwd, p))View on GitHub (pinned to 6c01d35af5)
Solutions
- Verify the filePath exists (fs.existsSync / lstat) before calling the parser and fix the path.
- If parsing a string, omit the filePath option or point it at the real source file so includes resolve.
- Use an absolute path to eliminate cwd ambiguity.
- Ensure any temp files written for parsing are not deleted before MJMLParser runs.
Example fix
// before
MJMLParser(mjmlString, { filePath: '/tmp/old.mjml' }) // deleted
// after
MJMLParser(mjmlString) // no includes, drop filePath
// or: { filePath: path.resolve('src/newsletter.mjml') } Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
function safeFilePathOption(p) {
if (!p) return {};
if (!fs.existsSync(p)) throw new Error(`filePath option does not exist: ${p}`);
return { filePath: p };
}
MJMLParser(xml, safeFilePathOption(filePath)); Try / catch
try {
return MJMLParser(xml, { filePath })
} catch (e) {
if (e.message === 'Specified filePath does not exist') {
console.error(`Bad filePath option: ${filePath}; cwd=${process.cwd()}`)
} else throw e
} Prevention
- Only pass filePath when parsing real on-disk files.
- Resolve include paths against absolute base directories.
- Keep temp source files alive until parsing completes.
When it happens
Trigger: Calling MJMLParser (directly, or via mjmlJson/partialMjml/mjml2html) with { filePath: '/nonexistent/path.mjml' } or a path that was deleted/moved; passing a filePath option while parsing a string whose 'source file' never existed on disk.
Common situations: Parsing MJML from a string but passing a filePath of a temp file already cleaned up; build tools deriving filePath from a request URL instead of a real file; directory renames between dev and CI.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- Specified filePath does not exist
- Circular inclusion detected on file : ${partialPath}
- Parsing failed. Check your mjml.
- Cannot read file: ${path} doesn't exist or no access
AI-assisted analysis of mjmlio/mjml@6c01d35af5 (2026-09-02).
Data as JSON: /api/errors/f4e20da07261a2d3.
Report an issue: GitHub.