n8n-io/n8n · error · InvalidRuntimeSkillError
Invalid skill at ${sourceDirectory}: ${formatSkillValidation
Error message
Invalid skill at ${sourceDirectory}: ${formatSkillValidationErrors(parsed.errors)} What it means
Thrown by loadRuntimeSkillsFromDirectory while parsing each SKILL.md via parseRuntimeSkillMarkdown. It fires when the file's YAML frontmatter or body fails parsing — e.g. missing frontmatter delimiters, invalid YAML, an unknown field, a name that breaks the skill-name pattern, or a missing required 'name'/'description'. The error lists the source directory and every validation message via formatSkillValidationErrors. Wrapped as InvalidRuntimeSkillError.
Source
Thrown at packages/@n8n/agents/src/skills/registry.ts:142
if (!existsSync(rootDir) || !statSync(rootDir).isDirectory()) return [];
return collectSkillFiles(rootDir).map((skillPath) => {
const skillDir = dirname(skillPath);
const sourceDirectory = toPosixPath(relative(rootDir, skillDir));
validateRuntimeSkillFolder(skillDir, skillPath, sourceDirectory);
const content = readFileSync(skillPath, 'utf-8');
const parsed = parseRuntimeSkillMarkdown(content, {
sourceName: basename(skillDir),
path: toPosixPath(skillPath),
sourcePath: toPosixPath(skillPath),
directory: toPosixPath(skillDir),
sourceDirectory,
category: categoryFor(sourceDirectory),
});
if (!parsed.ok) {
throw new InvalidRuntimeSkillError(
`Invalid skill at ${sourceDirectory}: ${formatSkillValidationErrors(parsed.errors)}`,
);
}
return {
...parsed.skill,
linkedFiles: loadLinkedFiles(skillDir),
};
});
}
export function formatSkillValidationErrors(
errors: Array<{ message: string; field?: string; path?: string; hint?: string }>,
): string {
return errors
.map((error) => {
const field = error.field ? ` field "${error.field}"` : '';
const path = error.path ? ` (${error.path})` : '';View on GitHub (pinned to 5ac6606e81)
Solutions
- Open the SKILL.md at the source directory named in the message and start the file with a `---`-delimited YAML frontmatter block.
- Ensure frontmatter has required `name` (lowercase, matching /^[a-z0-9][a-z0-9._-]{0,63}$/) and `description` (non-empty string), plus a non-empty instruction body below the closing `---`.
- Remove or rename any unknown frontmatter keys; move extension data into the `metadata` object if you need it.
- Re-run; the error message enumerates each remaining problem field-by-field.
Example fix
# skills/billing/SKILL.md (before) --- Name: Billing Descripton: Billing help --- # skills/billing/SKILL.md (after) --- name: billing description: Billing help --- Instructions for the billing skill.
Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'fs';
import { parseRuntimeSkillMarkdown } from '@n8n/agents/skills'; // or relative path
function prevalidateSkillFile(skillPath: string): void {
const content = readFileSync(skillPath, 'utf-8');
const result = parseRuntimeSkillMarkdown(content, { sourcePath: skillPath });
if (!result.ok) {
throw new Error(`Refusing to load ${skillPath}: ${JSON.stringify(result.errors)}`);
}
} Type guard
function hasValidFrontmatter(content: string): boolean {
const lines = content.split(/\r?\n/);
return lines[0]?.trim() === '---' && lines.slice(1).some((l, i) => i > 0 && l.trim() === '---');
}
if (!hasValidFrontmatter(readFileSync(skillPath, 'utf-8'))) {
throw new Error(`${skillPath} missing --- delimited YAML frontmatter`); Try / catch
import { InvalidRuntimeSkillError } from '@n8n/agents/skills';
try {
loadRuntimeSkillSourceFromDirectory(rootDir);
} catch (err) {
if (err instanceof InvalidRuntimeSkillError) {
// parse error — log and skip / fail the build, do not retry
console.error(`Skill load failed: ${err.message}`);
process.exit(1);
}
throw err;
} Prevention
- Add a CI step that loads the skills directory and fails the build on InvalidRuntimeSkillError before deploy.
- Use a skill template/scaffold so every new SKILL.md starts with correct frontmatter (name + description).
- Validate skill names against /^[a-z0-9][a-z0-9._-]{0,63}$/ at authoring time.
- Keep a reference of allowed frontmatter keys; reject unknown fields in your editor/CI lint.
When it happens
Trigger: A SKILL.md whose first line is not `---`; frontmatter missing the closing `---`; a YAML syntax error (bad indentation, unquoted colon); an unknown frontmatter key (typo like `descripton`); a `name` with uppercase letters, spaces, or >64 chars; a missing `name` or `description` field; body that is empty/whitespace (instructions required).
Common situations: Authoring a new skill and misspelling a frontmatter key; copy-pasting from a non-skill template that lacks frontmatter; editor auto-stripping the leading `---`; renaming a skill to a display name with spaces instead of the lowercase slug; CI loading a skill directory that contains a stale draft SKILL.md.
Related errors
- Model ID is required
- Invalid delegate sub-agent tool name "${name}": must start w
- ${toolName} requires resumeSubAgent and cancelSubAgent to be
- Tool name "${reservedTool.name}" is reserved for runtime ski
- toolCallConcurrency must be a positive integer or Infinity
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/ac4ab987dfffa12a.
Report an issue: GitHub.