google-gemini/gemini-cli · error
Duplicate skill name
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.
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
- Agent ' ' not found.
- Agent with name ' ' is already loaded.
- Empty command in auth value. Expected format: !command
- Invalid approval mode
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/b53a5d85f90bf858.
Report an issue: GitHub.
Appendix: 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)