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

  1. Verify the path exists and is a regular file: ls -l <path>, and check permissions
  2. Run the CLI from the correct working directory or use absolute paths
  3. Fix CI/package steps so template files are included and readable in the environment
  4. 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

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.