vercel/ai · error · Error
Invalid Cline skill name
Error message
Invalid Cline skill name: ${skill.name} What it means
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.
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.
Example fix
// before
prompt({ skills: [{ name: '../hostile', ... }] });
// after
const safeName = rawName.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/^-+|-+$/g, '');
prompt({ skills: [{ name: safeName || 'skill', ... }] }); Defensive patterns
Strategy: validation
Validate before calling
const CLINE_SKILL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/; // mirror harness pattern
function assertValidSkillName(name: string) {
if (!CLINE_SKILL_NAME_PATTERN.test(name) || name === '.' || name === '..') {
throw new Error(`Invalid skill name: ${name}`);
}
}
skills.forEach(s => assertValidSkillName(s.name)); Try / catch
try {
await session.prompt({ text, skills });
} catch (e) {
if (e instanceof Error && e.message.startsWith('Invalid Cline skill name')) {
const bad = e.message.split(': ')[1];
skills = skills.filter(s => s.name !== bad);
await session.prompt({ text, skills });
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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 '..'.
Common situations: 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.
Related errors
- maxEmbeddingsPerCall must be greater than 0
- ACP harnessId must be a stable kebab-case identifier…
- Cline did not create its native skills tool.
- Cline MCP server must define a transport, command, or URL.
- Duplicate skill name
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/dffc2a5b8bfda6cd.
Report an issue: GitHub.
Appendix: source
Thrown at packages/harness-cline/src/cline-skills.ts:96
return { signature, tool };
}
function projectClineSkills({
skills,
}: {
skills: ReadonlyArray<HarnessV1Skill>;
}): ReadonlyArray<ProjectedClineSkill> {
const names = new Set<string>();
const ids = new Set<string>();
return skills
.map(skill => {
if (
!CLINE_SKILL_NAME_PATTERN.test(skill.name) ||
skill.name === '.' ||
skill.name === '..'
) {
throw new Error(`Invalid Cline skill name: ${skill.name}`);
}
if (names.has(skill.name)) {
throw new Error(`Duplicate Cline skill name: ${skill.name}`);
}
names.add(skill.name);
const id = normalizeSkillToken(skill.name);
if (ids.has(id)) {
throw new Error(`Duplicate Cline skill identifier: ${id}`);
}
ids.add(id);
return {
id,
name: skill.name,
description: skill.description,
content: skill.content,
files: (skill.files ?? [])View on GitHub (pinned to 69428b1f8b)