{"record":{"id":"ac4ab987dfffa12a","repo":"n8n-io/n8n","slug":"invalid-skill-at-sourcedirectory-formatskill","errorCode":null,"errorMessage":"Invalid skill at ${sourceDirectory}: ${formatSkillValidationErrors(parsed.errors)}","messagePattern":"Invalid skill at (.+?): (.+?)","errorType":"exception","errorClass":"InvalidRuntimeSkillError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/skills/registry.ts","lineNumber":142,"sourceCode":"\tif (!existsSync(rootDir) || !statSync(rootDir).isDirectory()) return [];\n\n\treturn collectSkillFiles(rootDir).map((skillPath) => {\n\t\tconst skillDir = dirname(skillPath);\n\t\tconst sourceDirectory = toPosixPath(relative(rootDir, skillDir));\n\t\tvalidateRuntimeSkillFolder(skillDir, skillPath, sourceDirectory);\n\n\t\tconst content = readFileSync(skillPath, 'utf-8');\n\t\tconst parsed = parseRuntimeSkillMarkdown(content, {\n\t\t\tsourceName: basename(skillDir),\n\t\t\tpath: toPosixPath(skillPath),\n\t\t\tsourcePath: toPosixPath(skillPath),\n\t\t\tdirectory: toPosixPath(skillDir),\n\t\t\tsourceDirectory,\n\t\t\tcategory: categoryFor(sourceDirectory),\n\t\t});\n\n\t\tif (!parsed.ok) {\n\t\t\tthrow new InvalidRuntimeSkillError(\n\t\t\t\t`Invalid skill at ${sourceDirectory}: ${formatSkillValidationErrors(parsed.errors)}`,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\t...parsed.skill,\n\t\t\tlinkedFiles: loadLinkedFiles(skillDir),\n\t\t};\n\t});\n}\n\nexport function formatSkillValidationErrors(\n\terrors: Array<{ message: string; field?: string; path?: string; hint?: string }>,\n): string {\n\treturn errors\n\t\t.map((error) => {\n\t\t\tconst field = error.field ? ` field \"${error.field}\"` : '';\n\t\t\tconst path = error.path ? ` (${error.path})` : '';","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/skills/registry.ts#L124-L160","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# skills/billing/SKILL.md  (before)\n---\nName: Billing\nDescripton: Billing help\n---\n\n# skills/billing/SKILL.md  (after)\n---\nname: billing\ndescription: Billing help\n---\nInstructions for the billing skill.","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'fs';\nimport { parseRuntimeSkillMarkdown } from '@n8n/agents/skills'; // or relative path\n\nfunction prevalidateSkillFile(skillPath: string): void {\n  const content = readFileSync(skillPath, 'utf-8');\n  const result = parseRuntimeSkillMarkdown(content, { sourcePath: skillPath });\n  if (!result.ok) {\n    throw new Error(`Refusing to load ${skillPath}: ${JSON.stringify(result.errors)}`);\n  }\n}","typeGuard":"function hasValidFrontmatter(content: string): boolean {\n  const lines = content.split(/\\r?\\n/);\n  return lines[0]?.trim() === '---' && lines.slice(1).some((l, i) => i > 0 && l.trim() === '---');\n}\n\nif (!hasValidFrontmatter(readFileSync(skillPath, 'utf-8'))) {\n  throw new Error(`${skillPath} missing --- delimited YAML frontmatter`);","tryCatchPattern":"import { InvalidRuntimeSkillError } from '@n8n/agents/skills';\n\ntry {\n  loadRuntimeSkillSourceFromDirectory(rootDir);\n} catch (err) {\n  if (err instanceof InvalidRuntimeSkillError) {\n    // parse error — log and skip / fail the build, do not retry\n    console.error(`Skill load failed: ${err.message}`);\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["skills","validation","configuration","frontmatter"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}