vercel/ai · error · Error
Invalid skill file path for ${skillName}: ${filePath}
Error message
Invalid skill file path for ${skillName}: ${filePath} What it means
Thrown by normalizeSkillFilePath in the harness package when a skill file path fails safety validation after normalization. Paths that escape the skill directory ('..' traversal segments), are empty, are '.', or are absolute when a relative path is required are rejected. This prevents writing skill files outside the intended skill folder (path traversal).
Source
Thrown at packages/harness/src/utils/write-skills.ts:511
skillName?: string;
filePath: string;
mode: SkillFilePathMode;
message?: (input: { skillName: string; filePath: string }) => string;
}): string {
const normalized =
mode === 'strip-leading-slashes'
? filePath.replace(/^\/+/, '')
: path.posix.normalize(filePath);
const invalid =
normalized === '' ||
(mode === 'relative' && normalized === '.') ||
normalized.startsWith('../') ||
normalized.includes('/../') ||
normalized.endsWith('/..') ||
(mode === 'relative' && path.posix.isAbsolute(normalized));
if (invalid) {
throw new Error(
message?.({ skillName: skillName ?? '', filePath }) ??
`Invalid skill file path for ${skillName}: ${filePath}`,
);
}
return normalized;
}
function renderSkillFile({
skill,
trailingNewline,
}: {
skill: HarnessV1Skill;
trailingNewline: boolean;
}): string {
const content = `---\nname: ${skill.name}\ndescription: ${skill.description}\n---\n\n${skill.content}`;
return trailingNewline ? `${content}\n` : content;
}
View on GitHub (pinned to 69428b1f8b)
Solutions
- Remove any '..' segments, leading slashes, and empty/ '.' paths from the skill's file path so it is a plain relative path like 'reference.md' or 'scripts/run.sh'.
- If the path comes from user input, sanitize it first (normalize, reject absolute and traversal segments) before passing it to the skills API.
- Read the thrown message — it names the offending skillName and filePath — and correct that specific entry in the manifest.
Example fix
// before
skills: [{ name: 'my-skill', files: [{ path: '../../etc/hosts', content: '...' }] }]
// after
skills: [{ name: 'my-skill', files: [{ path: 'etc-notes.md', content: '...' }] }] Defensive patterns
Strategy: validation
Validate before calling
function isSafeSkillFilePath(p) {
const normalized = p.replace(/^\/+/, '');
return (
normalized !== '' && normalized !== '.' &&
!normalized.startsWith('../') && !normalized.includes('/../') &&
!normalized.endsWith('/..')
);
}
if (!isSafeSkillFilePath(skillFile.path)) throw new Error(`Refusing unsafe skill path: ${skillFile.path}`); Try / catch
try {
await projectSkills({ skills });
} catch (e) {
if (e instanceof Error && e.message.startsWith('Invalid skill file path')) {
console.error(`Fix skill manifest path: ${e.message}`);
} else throw e;
} Prevention
- Store skill file paths as plain relative paths (no leading '/', no '..') in manifests.
- Validate/sanitize user-supplied paths at input boundaries with path.posix.normalize before writing skills.
- Add a unit test asserting traversal paths ('../x', '/abs', '') are rejected.
When it happens
Trigger: Calling projectSkill (or write-skills APIs) with a skill whose file path: contains '../' segments, resolves to '' or '.', starts with '/', or equals/ends with '..'. Also triggered when mode is 'relative' and the normalized path is absolute.
Common situations: Programmatically generated skill manifests with user-supplied paths; migrating skills from another tool that used absolute paths; accidental '..' in a path template; empty file path fields in skill YAML frontmatter.
Related errors
- maxEmbeddingsPerCall must be greater than 0
- maxInputBytesPerCall must be greater than 0
- No image generated.
- No object generated: the model did not return a response.
- Invalid Cline history file name: ${historyFileName}
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/1d780973d7fdbe93.
Report an issue: GitHub.