{"record":{"id":"bb331937ea8dfce5","repo":"santifer/career-ops","slug":"template-filefor-chosen-missing-required-place","errorCode":null,"errorMessage":"Template ${fileFor(chosen)} missing required placeholders: ${v.missing.map((m) => `{{${m}}}`).join(', ')}","messagePattern":"Template (.+?) missing required placeholders: (.+?)\\}\\}`\\)\\.join\\(', '\\)\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cv-templates.mjs","lineNumber":154,"sourceCode":"\n  const explicit = Boolean(name && String(name).trim());\n  let chosen = kebab(explicit ? name : loadProfileDefault(kind, { profilePath }) || 'standard');\n  const fileFor = (n) => (n === 'standard' ? `${cfg.prefix}.${format}` : `${cfg.prefix}.${n}.${format}`);\n\n  let path = resolve(dir, fileFor(chosen));\n  if (!existsSync(path)) {\n    if (fallback && chosen !== 'standard') {\n      chosen = 'standard';\n      path = resolve(dir, fileFor(chosen));\n    }\n    if (!existsSync(path)) {\n      throw new Error(`Template not found for kind=${kind} name=${chosen} (${fileFor(chosen)})`);\n    }\n  }\n  if (format === 'html') {\n    const v = validateTemplate(path, kind);\n    if (!v.ok) {\n      throw new Error(\n        `Template ${fileFor(chosen)} missing required placeholders: ${v.missing.map((m) => `{{${m}}}`).join(', ')}`\n      );\n    }\n  }\n  return path;\n}\n\n// ---- CLI ----\nconst isMain = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);\nif (isMain) {\n  const argv = process.argv.slice(2);\n  const cmd = argv[0];\n  const kind = argv[1];\n  const flags = Object.fromEntries(\n    argv.filter((a) => a.startsWith('--')).map((a) => {\n      const [k, v] = a.replace(/^--/, '').split('=');\n      return [k, v ?? true];\n    })","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/cv-templates.mjs#L136-L172","documentation":"For HTML templates, resolveTemplate() runs validateTemplate(), which reads the file and checks it literally contains each required placeholder token: {{NAME}}, {{EXPERIENCE}}, {{EDUCATION}} for kind 'cv'; {{NAME}}, {{ROLE_TITLE}}, {{OPENING}} for kind 'cover'. The check is a plain substring match for the exact uppercase `{{TOKEN}}` spelling — any missing token is listed in the error. TeX templates (format: 'tex') skip this validation entirely, so the throw only happens on the html path.","triggerScenarios":"A custom cv-template.custom.html that renders experience with a different token like {{JOBS}} or {{experience}} (lowercase); a cover-letter template missing {{ROLE_TITLE}}; placeholders renamed when a designer handed over the HTML; template edited through a tool that HTML-escaped the double braces ({{ → &#123;&#123;).","commonSituations":"Bringing in a NiceCV/Canva-exported HTML layout that never had career-ops tokens; casing drift after hand-editing; someone 'cleaning up unused variables' from the template; copying a cover template as the base for a cv (each kind requires a different token set).","solutions":["Add each missing token listed in the message verbatim, uppercase, with double braces (e.g. {{EXPERIENCE}}) at the right spot in the template body","Copy templates/cv-template.html / cover-letter-template base file and restyle around its existing placeholders instead of starting from a foreign layout","Pre-check custom templates with validateTemplate(path, kind) — it returns { ok, missing } without throwing","If the token set genuinely changed for your fork, update the `required` array for that kind in KINDS (system-layer change)"],"exampleFix":"<!-- before -->\n<h1>Jane Doe</h1>\n<section class=\"jobs\">…hard-coded…</section>\n\n<!-- after -->\n<h1>{{NAME}}</h1>\n<section class=\"jobs\">{{EXPERIENCE}}</section>\n<section class=\"edu\">{{EDUCATION}}</section>","handlingStrategy":"validation","validationCode":"import { validateTemplate, KINDS } from './cv-templates.mjs';\nconst v = validateTemplate('templates/cv-template.fancy.html', 'cv');\nif (!v.ok) {\n  throw new Error(`custom template lacks placeholders: ${v.missing.join(', ')}`);\n}","typeGuard":"import { validateTemplate } from './cv-templates.mjs';\nconst templateIsValid = (path, kind) => validateTemplate(path, kind).ok;","tryCatchPattern":"try {\n  resolveTemplate('cv', 'fancy');\n} catch (err) {\n  if (err.message.includes('missing required placeholders')) {\n    // fall back to standard (which always has them) while the custom one is fixed\n    resolveTemplate('cv', 'standard');\n  } else throw err;\n}","preventionTips":["Start custom HTML from the shipped template so required tokens survive the restyle","Run validateTemplate() in a repo lint/test step over every templates/*.html so drift is caught at commit time, not render time","Keep tokens exactly uppercase with double braces — the check is literal substring matching, not a template engine"],"tags":["templates","validation","placeholders","html"],"backgroundTag":"schema-validation-failed","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","contentChangedAt":"2026-08-20T23:00:06.764Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}