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
- 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.
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
- 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.
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
- No valid skills found in "${sourcePath}". Ensure a SKILL.md
- The source argument must be provided.
- The path argument must be provided.
- The skill name must be provided.
- Invalid approval mode: ${rawApprovalMode}. Valid values are:
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/b53a5d85f90bf858.
Report an issue: GitHub.