{"record":{"id":"ef32ff38f2e925a3","repo":"paperclipai/paperclip","slug":"skill-creation-failed-response-status","errorCode":null,"errorMessage":"Skill creation failed (${response.status})","messagePattern":"Skill creation failed \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/skill-tools.ts","lineNumber":32,"sourceCode":"  if (!document.hasFrontmatter || !metadata.success || !document.body.trim()\n    || metadata.data.name !== input.name || metadata.data.description !== input.description\n    || (input.slug !== undefined && input.slug !== input.name)) {\n    context.addIssue({ code: \"custom\", path: [\"markdown\"], message: \"Provide a complete SKILL.md with name and description matching the tool inputs, a nonempty body, and slug equal to name when supplied.\" });\n  }\n});\n\n/** Use the same API and company skill policy as Skill Studio. */\nexport async function callCreateSkillTool(input: {\n  arguments: Record<string, unknown>; apiUrl: string; token: string; companyId: string;\n}, fetcher: typeof fetch = fetch) {\n  const body = createSkillToolInput.parse(input.arguments);\n  const response = await fetcher(`${input.apiUrl.replace(/\\/+$/, \"\").replace(/\\/api$/, \"\")}/api/companies/${encodeURIComponent(input.companyId)}/skills`, {\n    method: \"POST\",\n    headers: { Authorization: `Bearer ${input.token}`, \"Content-Type\": \"application/json\" },\n    body: JSON.stringify(body), signal: AbortSignal.timeout(60_000),\n  });\n  const result = await response.json();\n  if (!response.ok) throw new Error(typeof result.error === \"string\" ? result.error : `Skill creation failed (${response.status})`);\n  return { id: result.id, name: result.name, slug: result.slug, description: result.description,\n    versionId: result.currentVersionId, studioPath: `/skills/studio/${encodeURIComponent(result.id)}` };\n}\n","sourceCodeStart":14,"sourceCodeEnd":36,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/skill-tools.ts#L14-L36","documentation":"Generic Error thrown by callCreateSkillTool when the POST to /api/companies/:companyId/skills returns a non-2xx HTTP status and the response body does not contain a string error field. It wraps the HTTP status code in the fallback message 'Skill creation failed (<status>)'. It surfaces server-side rejection of the skill creation request (validation, auth, policy, or server failure) to the calling agent.","triggerScenarios":"POSTing skill creation to the Paperclip API and receiving response.ok === false, e.g. 401/403 (bad or expired bearer token, wrong company), 400/422 (frontmatter/body mismatch, name/slug rules, size limits), 409 (duplicate skill/slug), 5xx (server error), and result.error is not a string (or response.json() returned something unexpected).","commonSituations":"Agent-generated SKILL.md whose frontmatter name/description do not match the tool inputs; token lacking company skill-creation permission; apiUrl misconfigured so the request hits the wrong route (404); slug collision with an existing skill; server-side policy rejecting the content.","solutions":["Read the HTTP status in the message: 401/403 → fix the bearer token and company membership; 400/422 → fix the SKILL.md frontmatter so name/description match inputs and satisfy skillFrontmatterSchema; 409 → rename or reuse the existing skill; 5xx → retry after server recovery","Check response body in server logs — the fallback means the server did not return a usable error string; fix or handle the payload shape","Verify apiUrl is correct (no double /api, correct host) so the POST reaches the skills endpoint","Re-validate the input locally with createSkillToolInput (zod) before sending"],"exampleFix":"// before\nthrow new Error(`Skill creation failed (${response.status})`);\n// after\nthrow Object.assign(new Error(`Skill creation failed (${response.status}): ${JSON.stringify(result)}`), { status: response.status });\n// or fix the caller: ensure frontmatter name/description equal the tool inputs before POSTing","handlingStrategy":"try-catch","validationCode":"const parsed = createSkillToolInput.safeParse(args);\nif (!parsed.success) return { ok: false, stage: 'local-validation', issues: parsed.error.issues };","typeGuard":null,"tryCatchPattern":"try {\n  return await callCreateSkillTool({ arguments: args, apiUrl, token, companyId });\n} catch (e) {\n  const m = /Skill creation failed \\((\\d{3})\\)/.exec(e.message);\n  if (m) return { ok: false, status: Number(m[1]), hint: Number(m[1]) < 500 ? 'fix input or auth' : 'retry later' };\n  throw e;\n}","preventionTips":["Match frontmatter name/description to tool inputs exactly","Keep markdown under 200000 chars and body nonempty","Reuse an idempotencyKey deliberately to avoid 409 duplicates","Confirm the token is for the same companyId in the URL"],"tags":["http","api","skills","error-handling"],"backgroundTag":"http-error-response","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}