{"record":{"id":"8e82b9917925712d","repo":"santifer/career-ops","slug":"template-name-name-is-claimed-by-two-files-x-and-y-a-name","errorCode":null,"errorMessage":"Template name \"${name}\" is claimed by two files: ${x} and ${y}. A name must resolve to one template — rename one, or remove the one you no longer use.","messagePattern":"Template name \"(.+?)\" is claimed by two files: (.+?) and (.+?)\\. A name must resolve to one template — rename one, or remove the one you no longer use\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cv-templates.mjs","lineNumber":199,"sourceCode":"      if (parsed) claim(parsed, resolve(packDir, file), d.name);\n    }\n  }\n\n  return found;\n}\n\n// A template name resolves to exactly one file, enforced when it is discovered\n// rather than settled by a precedence rule.\n//\n// Precedence would have to pick a winner while both files exist and both look\n// correct — during a migration from a flat template to a pack, say — and the\n// loser would simply stop being rendered, silently, with nothing in the output\n// naming the file that won. Failing at discovery costs one clear error and\n// makes the ambiguity impossible to ship past.\nfunction assertNoCollision(name, a, b, dir) {\n  const rel = (p) => p.slice(dir.length + 1) || p;\n  const [x, y] = [rel(a), rel(b)].sort();\n  throw new Error(\n    `Template name \"${name}\" is claimed by two files: ${x} and ${y}. `\n      + `A name must resolve to one template — rename one, or remove the one you no longer use.`\n  );\n}\n\nexport function listTemplates(kind, { dir = DEFAULT_TEMPLATES_DIR, format = 'html' } = {}) {\n  const cfg = KINDS[kind];\n  if (!cfg) throw new Error(`Unknown template kind: ${kind}`);\n  assertFormat(format);\n  return [...discover(kind, { dir, format }).values()].sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport function validateTemplate(path, kind) {\n  const cfg = KINDS[kind];\n  if (!cfg) throw new Error(`Unknown template kind: ${kind}`);\n  const text = readFileSync(path, 'utf-8');\n  const missing = cfg.required.filter((ph) => !text.includes(`{{${ph}}}`));\n  return { ok: missing.length === 0, missing };","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/cv-templates.mjs#L181-L217","documentation":"assertNoCollision fires at template discovery when two different files in the templates directory resolve to the same template name (e.g. `cv-template.standard.html` and `cv-template.standard.tex` variants or a stray duplicate). Since a name must resolve to exactly one template, an ambiguous pair would mean one file silently stops being rendered, so discovery fails loudly with both conflicting paths (sorted, dir-relative) in the message.","triggerScenarios":"Adding `cv-template.standard.html` next to an existing file with the same base name; restoring a backup copy (`cv-template.old.html` vs edited duplicate) that parses to the same name; two custom templates named identically; a stale duplicate left after renaming one file only partially.","commonSituations":"Manual template experiments left in the templates dir; git merge results in duplicate files; following naming convention `cv-template.<name>.html` while a legacy file with the same <name> exists; syncing templates across machines creating duplicates.","solutions":["Rename one of the two files listed in the error so each name is unique.","Delete the template you no longer use.","Move unused/backup templates out of the templates directory.","After fixing, re-run listTemplates to confirm discovery succeeds."],"exampleFix":"// before: two files claim 'standard'\n// templates/cv-template.standard.html\n// templates/cv-template.standard.tex.bak.html  ← collides\n\n// after: rename or remove the duplicate\n// templates/cv-template.standard.html\n// templates/archive/cv-template.standard.tex.bak.html  (outside templates dir)","handlingStrategy":"validation","validationCode":"import { listTemplates } from './cv-templates.mjs';\nfunction assertUniqueNames(dir) {\n  const seen = new Map();\n  for (const t of listTemplates('cv', { dir })) {\n    if (seen.has(t.name)) throw new Error(`Duplicate template name '${t.name}' in ${dir}`);\n    seen.set(t.name, t.file);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const templates = listTemplates('cv');\n} catch (e) {\n  if (e.message.includes('is claimed by two files')) {\n    console.error(`Template collision in templates dir: ${e.message}\\nFix by renaming/removing one file.`);\n    process.exitCode = 1;\n    return [];\n  }\n  throw e;\n}","preventionTips":["Keep only active templates in the templates directory; archive unused ones elsewhere.","Check discovery output after adding or merging template files.","Name experiments distinctly (cv-template.experiment-foo.html).","Add a CI check that listTemplates resolves without throwing."],"tags":["template","naming-collision","discovery"],"backgroundTag":"file-already-exists","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}