santifer/career-ops · error · Error

invalid format "${entryFormat}" (use: ${validFormats.join(',

Error message

invalid format "${entryFormat}" (use: ${validFormats.join(', ')})

What it means

Each batch entry's format — or the batch-global format when the entry omits it — is lowercased and must be one of validFormats = ['a4', 'letter'] (generate-pdf.mjs:1239). Uppercase is fine ('A4' passes via toLowerCase), but any other paper size or a value like 'pdf' is rejected per-entry.

Source

Thrown at generate-pdf.mjs:1266

  } catch (err) {
    if (err?.code !== 'ENOENT') throw err;
  }
  // One profile governs the whole batch, so the declared order is read once
  // rather than per entry. Anchored to workspaceRoot for the same reason the
  // single render is: it is the anchor readStyleTokens() and the cv.md read
  // already use, so one profile.yml supplies every setting.
  const cvSectionOrder = readCvSectionOrder(resolve(workspaceRoot, 'config', 'profile.yml'));

  for (let i = 0; i < manifest.length; i++) {
    const spec = manifest[i];
    try {
      if (!spec || typeof spec.input !== 'string' || typeof spec.output !== 'string') {
        throw new Error('each entry needs a string "input" and "output"');
      }

      const entryFormat = (spec.format || globals.format).toLowerCase();
      if (!validFormats.includes(entryFormat)) {
        throw new Error(`invalid format "${entryFormat}" (use: ${validFormats.join(', ')})`);
      }

      const entryReport = (spec.reportNum ?? '').toString().trim();
      if (entryReport && !/^\d+$/.test(entryReport)) {
        throw new Error(`invalid reportNum "${entryReport}" (use the numeric report number)`);
      }

      // Resolve manifest-supplied input/output relative to the manifest's own
      // directory, not process.cwd(), so a manifest renders identically wherever
      // the batch is launched from. Absolute paths in the manifest still win
      // (resolve() ignores the base when the tail is absolute).
      const entryInput = resolve(manifestDir, spec.input);
      const entryOutput = resolve(manifestDir, spec.output);

      // Path-containment guards (realpath-based): keep the read and write inside
      // the tracker workspace even through a symlinked ancestor. A batch
      // manifest that escapes the workspace is malformed/tampered and is
      // recorded as a per-entry failure rather than read or written.

View on GitHub (pinned to 60398d6549)

Solutions

  1. Set the entry's format to 'a4' or 'letter', matching the destination market's paper size.
  2. If all entries share one size, remove per-entry format and set the global format once.
  3. Remember casing is tolerated but the value itself is a fixed two-value enum.

Example fix

// before
{ "input": "output/a.html", "output": "output/a.pdf", "format": "A5" }

// after
{ "input": "output/a.html", "output": "output/a.pdf", "format": "a4" }
Defensive patterns

Strategy: validation

Validate before calling

const VALID = ['a4', 'letter'];
const bad = manifest
  .filter(e => e.format !== undefined && !VALID.includes(String(e.format).toLowerCase()))
  .map(e => e.format);
if (bad.length) { console.error(`Invalid format(s) ${bad.join(', ')} — use: ${VALID.join(', ')}`); process.exit(1); }

Type guard

/** @param {unknown} v */
function isValidFormat(v) {
  return v === undefined || (typeof v === 'string' && ['a4', 'letter'].includes(v.toLowerCase()));
}

Prevention

When it happens

Trigger: Manifest entries with "format": "A5", "format": "legal", or "format": "pdf" (confusing file format with paper size); a typo like "lette"; a global format flag set to an invalid value that every entry inherits.

Common situations: Porting manifests from tools where 'a5' or 'legal' are valid; assuming format means the output file type; editor autocomplete inserting the wrong token.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20). Data as JSON: /api/errors/d3e8794488aa4062. Report an issue: GitHub.