paperclipai/paperclip · error · Error

Catalog skill not found: ${catalogRef}

Error message

Catalog skill not found: ${catalogRef}

What it means

Thrown by getCatalogSkill() after GET /api/skills/catalog/ref returns null/undefined for a non-empty ref. It means the server could not resolve the given catalog skill identifier (ID, key, or slug) in the app-shipped catalog. The CLI treats a null body as not-found rather than crashing on undefined fields.

Source

Thrown at cli/src/commands/client/skills.ts:600

  ctx: ResolvedClientContext,
  opts: CatalogBrowseOptions,
): Promise<CatalogSkill[]> {
  const params = new URLSearchParams();
  appendQueryParam(params, "kind", opts.kind);
  appendQueryParam(params, "category", opts.category);
  appendQueryParam(params, "q", opts.query);
  const query = params.toString();
  return (await ctx.api.get<CatalogSkill[]>(`/api/skills/catalog${query ? `?${query}` : ""}`)) ?? [];
}

async function getCatalogSkill(ctx: ResolvedClientContext, catalogRef: string): Promise<CatalogSkill> {
  const ref = catalogRef.trim();
  if (!ref) {
    throw new Error("Catalog skill reference is required.");
  }
  const detail = await ctx.api.get<CatalogSkill>(`/api/skills/catalog/ref?ref=${encodeURIComponent(ref)}`);
  if (!detail) {
    throw new Error(`Catalog skill not found: ${catalogRef}`);
  }
  return detail;
}

export function resolveCompanySkillReference(
  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;

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Run `paperclipai skills browse` to confirm the exact id/key/slug currently offered.
  2. Copy the identifier verbatim from the browse output.
  3. Verify you are pointing at the correct API base (PAPERCLIP_API_URL / --api-base) for the catalog you expect.

Example fix

// before
await run(["skills", "inspect", "old-skill-slug"]);
// after
const rows = await runJson(["skills", "browse", "--json"]);
const hit = rows.find(r => r.slug?.includes("skill"));
if (hit) await run(["skills", "inspect", hit.id]);
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await run(["skills", "inspect", ref]);
} catch (err) {
  if (err instanceof Error && /Catalog skill not found/.test(err.message)) {
    console.error(`${ref} is not in this catalog. Run \`skills browse\` first.`);
    process.exit(2);
  }
  throw err;
}

Prevention

When it happens

Trigger: `paperclipai skills inspect <ref>` where <ref> is a typo, an ID from a different Paperclip deployment/version, a slug that no longer exists, or a value that belongs to teams rather than skills.

Common situations: Catalog updated between sessions removing/renaming a skill, cross-environment confusion (staging vs prod catalogs), or mistyped keys. Also occurs if the API is unreachable in a way that resolves to an empty 2xx body.

Related errors


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