paperclipai/paperclip · error · Error
Ambiguous skill slug "${trimmed}". Use a skill ID or key ins
Error message
Ambiguous skill slug "${trimmed}". Use a skill ID or key instead. What it means
Thrown by resolveCompanySkillReference() when the trimmed reference matches no skill by id or key, but normalizes to a slug that two or more installed company skills share. The resolver refuses to guess; it requires an unambiguous id or key instead. Slug normalization lowercases and collapses non-alphanumerics to hyphens.
Source
Thrown at cli/src/commands/client/skills.ts:624
skills: CompanySkillReferenceTarget[],
reference: string,
): CompanySkillReferenceTarget {
const trimmed = reference.trim();
if (!trimmed) {
throw new Error("Skill reference is required.");
}
const byId = skills.find((skill) => skill.id === trimmed);
if (byId) return byId;
const byKey = skills.find((skill) => skill.key === trimmed);
if (byKey) return byKey;
const normalizedSlug = normalizeSkillSlug(trimmed);
const bySlug = skills.filter((skill) => skill.slug === normalizedSlug);
if (bySlug.length === 1 && bySlug[0]) return bySlug[0];
if (bySlug.length > 1) {
throw new Error(`Ambiguous skill slug "${trimmed}". Use a skill ID or key instead.`);
}
throw new Error(`Skill not found: ${reference}`);
}
async function resolveCompanySkill(
ctx: ResolvedClientContext,
reference: string,
): Promise<CompanySkillReferenceTarget> {
return resolveCompanySkillReference(await listCompanySkills(ctx), reference);
}
async function checkCompanySkills(
ctx: ResolvedClientContext,
skillRef: string | undefined,
): Promise<CompanySkillCheckRow[]> {
const skills = await listCompanySkills(ctx);
const selected = skillRef ? [resolveCompanySkillReference(skills, skillRef)] : skills;View on GitHub (pinned to 67001ec6eb)
Solutions
- Use the unique skill id from `skills list` (the `id` column) instead of the slug.
- Alternatively use the skill `key` shown in the list output.
- Long-term: rename or remove one of the colliding skills so slugs become unique.
Example fix
// before await run(["skills", "show", "my-skill"]); // after const rows = await runJson(["skills", "list", "--json"]); const match = rows.find(r => r.slug === "my-skill"); await run(["skills", "show", match.id]); // unambiguous
Defensive patterns
Strategy: validation
Validate before calling
const skills = await runJson(["skills", "list", "--json"]);
const slug = normalizeSlug(ref);
const matches = skills.filter(s => s.slug === slug);
if (matches.length > 1) {
throw new Error(`Ambiguous slug; use one of: ${matches.map(m => m.id).join(", ")}`);
}
// safe to use slug
function normalizeSlug(v: string) {
return v.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
} Type guard
function isUnambiguousSkill(skills: Array<{slug: string}>, slug: string): boolean {
return skills.filter(s => s.slug === slug).length === 1;
} Prevention
- Prefer the id from `skills list` over hand-typed slugs.
- Detect collisions up front by counting slug matches.
- Rename colliding skills so slugs become unique.
When it happens
Trigger: Two installed skills end up with the same normalized slug (e.g. one named "My Skill" and another imported as "my-skill"), and the user addresses them by that slug: `paperclipai skills show my-skill`.
Common situations: Importing a catalog skill with a custom --as slug that collides with an existing skill, or two catalog skills whose display names normalize identically. More likely after merges or cross-company imports.
Related errors
- Skill not found: ${reference}
- Agent not found: ${agentRef}
- Could not locate local Paperclip skills directory. Expected
- Selector '${selector}' is ambiguous (matches both an ID and
- Use either a skill reference or --all, not both.
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/d742995cfacde716.
Report an issue: GitHub.