{"record":{"id":"dffc2a5b8bfda6cd","repo":"vercel/ai","slug":"invalid-cline-skill-name-skill-name","errorCode":null,"errorMessage":"Invalid Cline skill name: ${skill.name}","messagePattern":"Invalid Cline skill name: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/harness-cline/src/cline-skills.ts","lineNumber":96,"sourceCode":"\n  return { signature, tool };\n}\n\nfunction projectClineSkills({\n  skills,\n}: {\n  skills: ReadonlyArray<HarnessV1Skill>;\n}): ReadonlyArray<ProjectedClineSkill> {\n  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 ?? [])","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-cline/src/cline-skills.ts#L78-L114","documentation":"projectClineSkills validates each skill's name against CLINE_SKILL_NAME_PATTERN and additionally rejects '.' and '..'. Names failing the pattern (which disallow path-hostile or malformed identifiers) throw this plain Error before any duplicate check. Cline maps skill names to directories, so unsafe names would escape or break the skills layout.","triggerScenarios":"Passing a skill in turnOpts.skills whose name is empty, contains characters outside the allowed pattern (e.g. spaces, slashes, 'my skill/', '../escape'), or is exactly '.' or '..'.","commonSituations":"Generating skill names from user input or file paths without normalization; loading skills from a directory and using raw folder names including spaces or unicode; hand-written skill definitions with typos or separators in the name.","solutions":["Rename the offending skill so its name matches CLINE_SKILL_NAME_PATTERN (kebab-case-ish, no separators, not '.' or '..').","Sanitize names at load time: derive the name from the filename, trim, replace invalid characters with '-', and skip entries like '.'/'..'.","Add a preflight filter in your skill loader that mirrors the harness regex so invalid skills are rejected early with a clearer message."],"exampleFix":"// before\nprompt({ skills: [{ name: '../hostile', ... }] });\n// after\nconst safeName = rawName.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/^-+|-+$/g, '');\nprompt({ skills: [{ name: safeName || 'skill', ... }] });","handlingStrategy":"validation","validationCode":"const CLINE_SKILL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/; // mirror harness pattern\nfunction assertValidSkillName(name: string) {\n  if (!CLINE_SKILL_NAME_PATTERN.test(name) || name === '.' || name === '..') {\n    throw new Error(`Invalid skill name: ${name}`);\n  }\n}\nskills.forEach(s => assertValidSkillName(s.name));","typeGuard":null,"tryCatchPattern":"try {\n  await session.prompt({ text, skills });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Invalid Cline skill name')) {\n    const bad = e.message.split(': ')[1];\n    skills = skills.filter(s => s.name !== bad);\n    await session.prompt({ text, skills });\n  } else throw e;\n}","preventionTips":["Normalize skill names to kebab-case when loading from disk; never use raw directory names.","Skip '.', '..' and hidden entries when enumerating skill folders.","Run the harness name regex against every skill in your skill loader's unit tests."],"tags":["validation","naming","skills","cline"],"backgroundTag":"invalid-identifier-name","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}