santifer/career-ops · warning

ENOENT

ENOENT

Error message

⚠️  Font file not found, keeping original reference: fonts/${name}

What it means

generate-pdf.mjs inlines font files referenced by an HTML CV as base64 data URLs before rendering with Playwright. When a font file referenced as fonts/<name> does not exist on disk, the read throws ENOENT; the code deliberately downgrades it to a warning and keeps the original CSS reference, so the PDF renders with a fallback font (or a broken reference) instead of the intended typeface.

Solutions

  1. Place the missing font file at the referenced path: find the <name> from the warning and copy that exact filename into fonts/ next to generate-pdf.mjs
  2. Run the script from the repo root so the relative fonts/ path resolves
  3. Update the HTML/CSS template to reference an installed system font (e.g. 'Arial', sans-serif) instead of fonts/<name> if vendoring is not desired

Example fix

// before (fonts dir incomplete)
cv-template.html: @font-face { src: url('fonts/Inter-Bold.woff2'); } // file absent -> ENOENT
// after
cp ~/Downloads/Inter-Bold.woff2 fonts/Inter-Bold.woff2
Defensive patterns

Strategy: fallback

Validate before calling

import { accessSync, constants } from 'node:fs';
function fontFileExists(fontPath) {
  try { accessSync(fontPath, constants.R_OK); return true; } catch { return false; }
}
if (!fontFileExists(`fonts/${name}`)) console.warn(`Font missing: fonts/${name}`);

Try / catch

try {
  const buf = await fs.readFile(fontPath);
  // inline as data URL
} catch (err) {
  if (err?.code === 'ENOENT') return keepOriginalReference();
  throw err;
}

Prevention

When it happens

Trigger: FONT_REF replacement loop calls fs read on a fonts/<name> path that does not exist — cv.md/template references a font family (e.g. fonts/Inter-Regular.woff2) that was never placed in the fonts/ directory, the font file was renamed or deleted, or the script runs from a working directory where fonts/ is not present.

Common situations: Using a custom cv-template.html that names a specific font but only vendoring part of the family (missing weights); cloning the repo without the fonts directory (gitignored binaries); running generate-pdf.mjs from a different cwd than the repo root so relative paths resolve wrong.

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 santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/a209c415216c5853. Report an issue: GitHub.

Appendix: source

Thrown at generate-pdf.mjs:1606

    const fontPath = resolve(fontsDir, name);
    const rel = relative(fontsDir, fontPath);
    if (rel.startsWith('..') || isAbsolute(rel)) {
      console.warn(`⚠️  Font reference escapes fonts/, keeping original reference: ${name}`);
      continue;
    }
    if (_fontDataUrlCache.has(fontPath)) {
      dataUrls.set(name, _fontDataUrlCache.get(fontPath));
      continue;
    }
    try {
      const buf = await readFile(fontPath);
      const ext = name.slice(name.lastIndexOf('.') + 1).toLowerCase();
      const dataUrl = `url('data:${MIME[ext] || 'application/octet-stream'};base64,${buf.toString('base64')}')`;
      _fontDataUrlCache.set(fontPath, dataUrl);
      dataUrls.set(name, dataUrl);
    } catch (err) {
      if (err?.code !== 'ENOENT') throw err;
      console.warn(`⚠️  Font file not found, keeping original reference: fonts/${name}`);
    }
  }
  return html.replace(FONT_REF, (match, _quote, name) => dataUrls.get(name) || match);
}

/**
 * Render an HTML string to a PDF file via headless Chromium.
 *
 * Writes the HTML to a temporary file in the baseDir and loads it via
 * page.goto() to give the page a file:// origin. This allows relative
 * resources (images, fonts) to load — setContent() runs from about:blank
 * and Chromium blocks file:// subresource loads from non-file origins.
 *
 * Local url('./fonts/...') references are inlined as data: URLs first so
 * fonts also survive the ATS normalization pass (which may strip font refs).
 *
 * @param {string} html - Full HTML document to render.
 * @param {string} outputPath - Absolute path to write the PDF to.

View on GitHub (pinned to aac998c7ed)