santifer/career-ops · error

Unsupported template format

Error message

Unsupported template format: ${format} (expected html or tex)

What it means

assertFormat allowlists template formats to exactly 'html' or 'tex'. Because the raw format string flows into template file path construction, an unvalidated value like `../../etc/passwd` would enable path traversal out of the templates directory, so the resolver fails fast with this error for anything outside the set.

Solutions

  1. Use format 'html' or 'tex' only.
  2. Normalize/validate user-supplied format values before passing them to the resolver.
  3. Fix case ('HTML' → 'html', 'TEX' → 'tex').
  4. If path-traversal input is the trigger, treat it as hostile input — the error is the intended security guard.

Example fix

// before
loadTemplate('cv', { format: userInput }); // userInput = '../../etc/passwd'

// after
const format = ['html', 'tex'].includes(userInput) ? userInput : 'html';
loadTemplate('cv', { format });
Defensive patterns

Strategy: validation

Validate before calling

const VALID_FORMATS = new Set(['html', 'tex']);
function safeFormat(input) {
  return VALID_FORMATS.has(input) ? input : 'html';
}

Type guard

const isTemplateFormat = (v) => v === 'html' || v === 'tex';

Try / catch

try {
  const tpl = loadTemplate(kind, { format });
} catch (e) {
  if (e.message.startsWith('Unsupported template format:')) {
    console.error(`${e.message}; refusing to build`);
    process.exitCode = 2;
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling listTemplates/loadTemplate with format='pdf', 'HTML', or a user-controlled string like `--format=../../etc/passwd` reaching assertFormat; an unvalidated CLI/env value passed as the format option.

Common situations: Users attempting unsupported output formats; case-mismatched input; security testing or accidental traversal payloads in the format option; refactors that renamed the tex format.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/47826fd00e34c472. Report an issue: GitHub.

Appendix: source

Thrown at cv-templates.mjs:53

    .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
    .join(' ');
}

export function kebab(display) {
  return String(display)
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
}

// The only template formats the resolver recognizes. `format` reaches path
// construction (fileFor) unmodified, so it must be allowlisted or a value like
// `--format=../../etc/passwd` would traverse out of the templates dir.
const VALID_FORMATS = new Set(['html', 'tex']);
function assertFormat(format) {
  if (!VALID_FORMATS.has(format)) {
    throw new Error(`Unsupported template format: ${format} (expected html or tex)`);
  }
}

// filename → {name, format} | null. Base "cv-template.html" → name "standard";
// "cv-template.<name>.html" → that name. Only html/tex are recognized.
function parseFilename(prefix, file) {
  const m = file.match(new RegExp(`^${prefix}(?:\\.([a-z0-9-]+))?\\.(html|tex)$`));
  if (!m) return null;
  return { name: m[1] || 'standard', format: m[2] };
}

export function parseMeta(path) {
  let text;
  try {
    text = readFileSync(path, 'utf-8');
  } catch {
    return {};
  }

View on GitHub (pinned to aac998c7ed)