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

  1. Use the unique skill id from `skills list` (the `id` column) instead of the slug.
  2. Alternatively use the skill `key` shown in the list output.
  3. 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

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


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/d742995cfacde716. Report an issue: GitHub.