google-gemini/gemini-cli · error

Duplicate skill name "${skill.name}" found at multiple locat

Error message

Duplicate skill name "${skill.name}" found at multiple locations:
  - ${seenNames.get(skill.name)}
  - ${skill.location}

What it means

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.

Source

Thrown at packages/cli/src/utils/skillUtils.ts:235

    targetDir: string,
  ) => Promise<boolean> = () => Promise.resolve(true),
): Promise<Array<{ name: string; location: string }>> {
  const sourcePath = path.resolve(source);

  onLog(`Searching for skills in ${sourcePath}...`);
  const skills = await loadSkillsFromDir(sourcePath);

  if (skills.length === 0) {
    throw new Error(
      `No valid skills found in "${sourcePath}". Ensure a SKILL.md file exists with valid frontmatter.`,
    );
  }

  // Check for internal name collisions
  const seenNames = new Map<string, string>();
  for (const skill of skills) {
    if (seenNames.has(skill.name)) {
      throw new Error(
        `Duplicate skill name "${skill.name}" found at multiple locations:\n  - ${seenNames.get(skill.name)}\n  - ${skill.location}`,
      );
    }
    seenNames.set(skill.name, skill.location);
  }

  const workspaceDir = process.cwd();
  const storage = new Storage(workspaceDir);
  const targetDir =
    scope === 'workspace'
      ? storage.getProjectSkillsDir()
      : Storage.getUserSkillsDir();

  if (!(await requestConsent(skills, targetDir))) {
    throw new Error('Skill linking cancelled by user.');
  }

  const resolvedTarget = path.resolve(targetDir);

View on GitHub (pinned to 5024443c72)

Solutions

  1. Read the two paths printed in the error and open each SKILL.md.
  2. Edit the `name` field in one of them so both skills have a unique kebab-case name.
  3. If one copy is stale, delete it from the source tree and re-run the link command.
  4. 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.

Example fix

// before — two SKILL.md files both contain:
// ---
// name: code-reviewer
// ---

// after — rename one:
// ---
// name: pr-reviewer
// ---
Defensive patterns

Strategy: validation

Validate before calling

function assertUniqueSkillNames(skills: { name: string; location: string }[]) {
  const seen = new Map<string, string>();
  for (const s of skills) {
    if (seen.has(s.name)) {
      throw new Error(`Duplicate name ${s.name}: ${seen.get(s.name)} vs ${s.location}`);
    }
    seen.set(s.name, s.location);
  }
}

// before install:
const skills = await loadSkillsFromDir(sourcePath);
assertUniqueSkillNames(skills);

Try / catch

try {
  await linkSkills(source, scope, onLog, consent);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Duplicate skill name')) {
    // parse the two paths, prompt user to rename one, then retry
  }
  throw e;
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/b53a5d85f90bf858. Report an issue: GitHub.