{"record":{"id":"d63e5c481b244e61","repo":"santifer/career-ops","slug":"unresolved-placeholders-new-set-unresolved","errorCode":null,"errorMessage":"Unresolved placeholders: ${[...new Set(unresolved)].join(', ')}","messagePattern":"Unresolved placeholders: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"build-cv-html.mjs","lineNumber":635,"sourceCode":"\n  const { substitutions, candidate } = renderReport(payload, partials);\n\n  // The contact row and photo carry conditional markup (dropped separators /\n  // no <img>), so they are rebuilt as whole blocks before placeholder fill.\n  let html = template.replace(CONTACT_ROW_RE, () => buildContactRow(candidate));\n  html = html.replace(/\\{\\{PHOTO\\}\\}/g, () => buildPhoto(candidate, candidate.name));\n\n  // Drop the optional sections (projects, education) that have no entries, so\n  // an absent one leaves no bare header behind. See cv-sections-core.mjs.\n  html = stripEmptySections(html, payload, 'html');\n\n  for (const [key, value] of Object.entries(substitutions)) {\n    html = html.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'), () => value);\n  }\n\n  const unresolved = html.match(PLACEHOLDER_RE);\n  if (unresolved) {\n    throw new Error(`Unresolved placeholders: ${[...new Set(unresolved)].join(', ')}`);\n  }\n  return html;\n}\n\nfunction countBullets(payload) {\n  const ex = Array.isArray(payload.experience)\n    ? payload.experience.flatMap(e => (Array.isArray(e?.bullets) ? e.bullets : []))\n    : [];\n  return ex.length;\n}\n\nasync function writeAndReport(html, absOutput, payload, extra = {}) {\n  const outDir = dirname(absOutput);\n  if (!existsSync(outDir)) await mkdir(outDir, { recursive: true });\n  await writeFile(absOutput, html, 'utf-8');\n\n  const fileInfo = await stat(absOutput);\n  const report = {","sourceCodeStart":617,"sourceCodeEnd":653,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/build-cv-html.mjs#L617-L653","documentation":"Error \"Unresolved placeholders: ${[...new Set(unresolved)].join(', ')}\" thrown in santifer/career-ops.","triggerScenarios":"Thrown at build-cv-html.mjs:635 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Add values for the listed placeholders in cv.md or the profile config, then rebuild.","Or remove the placeholders from the CV template if they are not used."],"exampleFix":"# fill {{email}} and {{location}} in config/profile.yml, then re-run build-cv-html.mjs","handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}