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
- Run `paperclipai skills browse` to confirm the exact id/key/slug currently offered.
- Copy the identifier verbatim from the browse output.
- 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
- Verify the ref against `skills browse` before inspect.
- Pin the API base to the catalog you expect.
- Treat not-found as a recoverable branch in automation, not a hard crash.
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
- Catalog team not found: ${catalogRef}
- Catalog skill reference is required.
- Skill not found: ${reference}
- No update status returned for skill ${skill.key}.
- No audit result returned for skill ${skill.key}.
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/b1f19ab4d4e8aeec.
Report an issue: GitHub.