santifer/career-ops · error

Template name " " is claimed by two files: and . A name…

Error message

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.

What it means

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.

Solutions

  1. Rename one of the two files listed in the error so each name is unique.
  2. Delete the template you no longer use.
  3. Move unused/backup templates out of the templates directory.
  4. After fixing, re-run listTemplates to confirm discovery succeeds.

Example fix

// before: two files claim 'standard'
// templates/cv-template.standard.html
// templates/cv-template.standard.tex.bak.html  ← collides

// after: rename or remove the duplicate
// templates/cv-template.standard.html
// templates/archive/cv-template.standard.tex.bak.html  (outside templates dir)
Defensive patterns

Strategy: validation

Validate before calling

import { listTemplates } from './cv-templates.mjs';
function assertUniqueNames(dir) {
  const seen = new Map();
  for (const t of listTemplates('cv', { dir })) {
    if (seen.has(t.name)) throw new Error(`Duplicate template name '${t.name}' in ${dir}`);
    seen.set(t.name, t.file);
  }
}

Try / catch

try {
  const templates = listTemplates('cv');
} catch (e) {
  if (e.message.includes('is claimed by two files')) {
    console.error(`Template collision in templates dir: ${e.message}\nFix by renaming/removing one file.`);
    process.exitCode = 1;
    return [];
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at cv-templates.mjs:199

      if (parsed) claim(parsed, resolve(packDir, file), d.name);
    }
  }

  return found;
}

// A template name resolves to exactly one file, enforced when it is discovered
// rather than settled by a precedence rule.
//
// Precedence would have to pick a winner while both files exist and both look
// correct — during a migration from a flat template to a pack, say — and the
// loser would simply stop being rendered, silently, with nothing in the output
// naming the file that won. Failing at discovery costs one clear error and
// makes the ambiguity impossible to ship past.
function assertNoCollision(name, a, b, dir) {
  const rel = (p) => p.slice(dir.length + 1) || p;
  const [x, y] = [rel(a), rel(b)].sort();
  throw new Error(
    `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.`
  );
}

export function listTemplates(kind, { dir = DEFAULT_TEMPLATES_DIR, format = 'html' } = {}) {
  const cfg = KINDS[kind];
  if (!cfg) throw new Error(`Unknown template kind: ${kind}`);
  assertFormat(format);
  return [...discover(kind, { dir, format }).values()].sort((a, b) => a.name.localeCompare(b.name));
}

export function validateTemplate(path, kind) {
  const cfg = KINDS[kind];
  if (!cfg) throw new Error(`Unknown template kind: ${kind}`);
  const text = readFileSync(path, 'utf-8');
  const missing = cfg.required.filter((ph) => !text.includes(`{{${ph}}}`));
  return { ok: missing.length === 0, missing };

View on GitHub (pinned to aac998c7ed)