{"record":{"id":"b53a5d85f90bf858","repo":"google-gemini/gemini-cli","slug":"duplicate-skill-name-skill-name-found-at-mult","errorCode":null,"errorMessage":"Duplicate skill name \"${skill.name}\" found at multiple locations:\n  - ${seenNames.get(skill.name)}\n  - ${skill.location}","messagePattern":"Duplicate skill name \"(.+?)\" found at multiple locations:\n  - (.+?)\n  - (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/skillUtils.ts","lineNumber":235,"sourceCode":"    targetDir: string,\n  ) => Promise<boolean> = () => Promise.resolve(true),\n): Promise<Array<{ name: string; location: string }>> {\n  const sourcePath = path.resolve(source);\n\n  onLog(`Searching for skills in ${sourcePath}...`);\n  const skills = await loadSkillsFromDir(sourcePath);\n\n  if (skills.length === 0) {\n    throw new Error(\n      `No valid skills found in \"${sourcePath}\". Ensure a SKILL.md file exists with valid frontmatter.`,\n    );\n  }\n\n  // Check for internal name collisions\n  const seenNames = new Map<string, string>();\n  for (const skill of skills) {\n    if (seenNames.has(skill.name)) {\n      throw new Error(\n        `Duplicate skill name \"${skill.name}\" found at multiple locations:\\n  - ${seenNames.get(skill.name)}\\n  - ${skill.location}`,\n      );\n    }\n    seenNames.set(skill.name, skill.location);\n  }\n\n  const workspaceDir = process.cwd();\n  const storage = new Storage(workspaceDir);\n  const targetDir =\n    scope === 'workspace'\n      ? storage.getProjectSkillsDir()\n      : Storage.getUserSkillsDir();\n\n  if (!(await requestConsent(skills, targetDir))) {\n    throw new Error('Skill linking cancelled by user.');\n  }\n\n  const resolvedTarget = path.resolve(targetDir);","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/cli/src/utils/skillUtils.ts#L217-L253","documentation":"Thrown during skill linking after the directory scan but before any files are written. The loader found two or more skills that resolved to the same internal `name` field (read from each SKILL.md frontmatter). Because skills are stored and referenced by name, duplicates would silently overwrite each other, so the install aborts with both colliding locations listed.","triggerScenarios":"Two SKILL.md files inside the scanned source tree declare an identical `name` in their frontmatter. This includes nested layouts where a parent skill and a vendored copy under a subfolder both carry the same name, or a monorepo where multiple skill packages were not given distinct names.","commonSituations":"Copying a skill folder to refactor it and forgetting to rename the `name` field; bundling upstream skills together where two authors happened to pick the same name; a symlink loop causing the same SKILL.md to be discovered twice under different paths; merging skill collections without deduping.","solutions":["Read the two paths printed in the error and open each SKILL.md.","Edit the `name` field in one of them so both skills have a unique kebab-case name.","If one copy is stale, delete it from the source tree and re-run the link command.","If you intentionally want to replace an installed skill, remove the old entry from the target skills directory first so only one candidate exists in the source."],"exampleFix":"// before — two SKILL.md files both contain:\n// ---\n// name: code-reviewer\n// ---\n\n// after — rename one:\n// ---\n// name: pr-reviewer\n// ---","handlingStrategy":"validation","validationCode":"function assertUniqueSkillNames(skills: { name: string; location: string }[]) {\n  const seen = new Map<string, string>();\n  for (const s of skills) {\n    if (seen.has(s.name)) {\n      throw new Error(`Duplicate name ${s.name}: ${seen.get(s.name)} vs ${s.location}`);\n    }\n    seen.set(s.name, s.location);\n  }\n}\n\n// before install:\nconst skills = await loadSkillsFromDir(sourcePath);\nassertUniqueSkillNames(skills);","typeGuard":null,"tryCatchPattern":"try {\n  await linkSkills(source, scope, onLog, consent);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Duplicate skill name')) {\n    // parse the two paths, prompt user to rename one, then retry\n  }\n  throw e;\n}","preventionTips":["Enforce a unique-name constraint at authoring time via a registry/monorepo lint rule.","When vendoring upstream skills, namespace their names with a prefix to avoid collisions.","Run a dedupe check in pre-publish scripts."],"tags":["skills","config","validation","naming"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}