{"record":{"id":"8779d5e66716fb4c","repo":"vercel/ai","slug":"duplicate-cline-skill-identifier-id","errorCode":null,"errorMessage":"Duplicate Cline skill identifier: ${id}","messagePattern":"Duplicate Cline skill identifier: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/harness-cline/src/cline-skills.ts","lineNumber":105,"sourceCode":"  const names = new Set<string>();\n  const ids = new Set<string>();\n  return skills\n    .map(skill => {\n      if (\n        !CLINE_SKILL_NAME_PATTERN.test(skill.name) ||\n        skill.name === '.' ||\n        skill.name === '..'\n      ) {\n        throw new Error(`Invalid Cline skill name: ${skill.name}`);\n      }\n      if (names.has(skill.name)) {\n        throw new Error(`Duplicate Cline skill name: ${skill.name}`);\n      }\n      names.add(skill.name);\n\n      const id = normalizeSkillToken(skill.name);\n      if (ids.has(id)) {\n        throw new Error(`Duplicate Cline skill identifier: ${id}`);\n      }\n      ids.add(id);\n\n      return {\n        id,\n        name: skill.name,\n        description: skill.description,\n        content: skill.content,\n        files: (skill.files ?? [])\n          .map(file => ({\n            path: normalizeSkillFilePath({\n              skillName: skill.name,\n              filePath: file.path,\n            }),\n            content: file.content,\n          }))\n          .sort(\n            (left, right) =>","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-cline/src/cline-skills.ts#L87-L123","documentation":"After validating unique skill names, projectClineSkills derives a normalized identifier via normalizeSkillToken(skill.name) and requires ids to be unique too. Two distinct names can normalize to the same token (e.g. 'My Skill' and 'my-skill'), which would create ambiguous skill ids, so it throws.","triggerScenarios":"projectClineSkills receives skills with distinct names that normalize to the same token, e.g. 'Deploy Helper' and 'deploy-helper' or 'deploy_helper' and 'deploy-helper'.","commonSituations":"Skills authored in different naming conventions (Title Case vs kebab-case) that collide after token normalization; importing the same skill from two sources under slightly different names.","solutions":["Rename one skill so its normalized token differs (change casing, spacing, or punctuation).","Check the colliding id printed in the message and inspect which names map to it.","Pre-normalize skill names (kebab-case) across all skill sources before registering them."],"exampleFix":"// before\nskills named 'Code Review' and 'code-review' both registered\n// after\n// rename one, e.g. 'Code Review' -> 'code-review-strict'","handlingStrategy":"validation","validationCode":"const ids = new Set();\nfor (const s of skills) {\n  const id = s.name.toLowerCase().replace(/[^a-z0-9]+/g, '-');\n  if (ids.has(id)) throw new Error(`colliding skill id: ${id}`);\n  ids.add(id);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await projectedSkills(input);\n} catch (e) {\n  if (/Duplicate Cline skill identifier/.test(String(e?.message))) {\n    // rename skill to avoid token collision, then retry\n  } else throw e;\n}","preventionTips":["Author all skill names in kebab-case so name and id match","Pre-normalize names with the same rule the harness uses","Avoid names differing only by casing, spaces, or underscores"],"tags":["harness-cline","skills","normalization","duplicate-id"],"backgroundTag":"duplicate-resource-name","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}