mjmlio/mjml · error
Cannot read file: ${path} doesn't exist or no access
Error message
Cannot read file: ${path} doesn't exist or no access What it means
readFile attempts fs.readFileSync(path) and, when the file cannot be read (missing file, no permission, it is a directory), warns 'Cannot read file: <path> doesn't exist or no access' and returns an empty object instead of throwing. The CLI then typically treats this as a missing/empty input.
Source
Thrown at packages/mjml-cli/src/commands/readFile.js:13
import fs from 'fs'
import { sync } from 'glob'
import { flatMap } from 'lodash'
export const flatMapPaths = (paths) =>
flatMap(paths, (p) => sync(p, { nodir: true }))
export default (path) => {
try {
return { file: path, mjml: fs.readFileSync(path).toString() }
} catch (e) {
// eslint-disable-next-line
console.warn(`Cannot read file: ${path} doesn't exist or no access`, e)
return {}
}
}
View on GitHub (pinned to 6c01d35af5)
Solutions
- Verify the path exists and is a regular file: ls -l <path>, and check permissions
- Run the CLI from the correct working directory or use absolute paths
- Fix CI/package steps so template files are included and readable in the environment
- If consuming readFile programmatically, check the returned object for a truthy mjml property before rendering
Example fix
// before
mjml emails/invite.mjml // ENOENT: file not copied
// after
mjml src/templates/invite.mjml --config.includePath src/templates/partials
// programmatic guard
const { mjml } = readFile(p)
if (!mjml) { throw new Error(`Input missing: ${p}`) } Defensive patterns
Strategy: validation
Validate before calling
import fs from 'fs'
const canRead = (p) => { try { return fs.statSync(p).isFile() } catch { return false } }
if (!canRead(inputPath)) throw new Error(`Input file missing or unreadable: ${inputPath}`) Type guard
const isReadableFile = (p) => { try { return typeof p === 'string' && fs.statSync(p).isFile() } catch { return false } } Try / catch
const { mjml } = readFile(p)
if (!mjml) {
// readFile swallows the error and returns {}
throw new Error(`Failed to read input: ${p}`)
} Prevention
- Verify paths exist with fs.statSync before rendering
- Use absolute paths (path.resolve) in scripts/CI
- Check file permissions in containers (COPY with correct owner)
- Treat an empty readFile return object as an error condition
When it happens
Trigger: Calling readFile('/some/path') where the file does not exist, the path is a directory, or the process lacks read permission. Any fs.readFileSync throw (ENOENT, EACCES, EISDIR) lands in this catch.
Common situations: Typo in the CLI input filename; globbing produced no matches and a literal pattern was passed; running in a container/CI where the template file was not copied or lacks permissions; wrong working directory.
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
AI-assisted analysis of mjmlio/mjml@6c01d35af5 (2026-09-02).
Data as JSON: /api/errors/ac449fe6ba1ed9c8.
Report an issue: GitHub.