{"record":{"id":"1b2fee9dd7cd9708","repo":"Yeachan-Heo/oh-my-codex","slug":"filepath-must-start-with-yaml-frontmatter-conta","errorCode":null,"errorMessage":"${filePath} must start with YAML frontmatter containing non-empty name and description fields","messagePattern":"(.+?) must start with YAML frontmatter containing non-empty name and description fields","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/setup.ts","lineNumber":2252,"sourceCode":"\t\treturn unquoted;\n\t}\n\n\tconst unquoted = trimmed.replace(/\\s+#.*$/, \"\").trim();\n\tif (!unquoted) {\n\t\tthrow new Error(`${filePath} frontmatter \"${key}\" must not be empty`);\n\t}\n\treturn unquoted;\n}\n\nexport function parseSkillFrontmatter(\n\tcontent: string,\n\tfilePath = \"SKILL.md\",\n): SkillFrontmatterMetadata {\n\tconst frontmatterMatch = content.match(\n\t\t/^---\\r?\\n([\\s\\S]*?)\\r?\\n---(?:\\r?\\n|$)/,\n\t);\n\tif (!frontmatterMatch) {\n\t\tthrow new Error(\n\t\t\t`${filePath} must start with YAML frontmatter containing non-empty name and description fields`,\n\t\t);\n\t}\n\n\tlet name: string | undefined;\n\tlet description: string | undefined;\n\tconst lines = frontmatterMatch[1].split(/\\r?\\n/);\n\n\tfor (const [index, rawLine] of lines.entries()) {\n\t\tconst line = rawLine.trimEnd();\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed || trimmed.startsWith(\"#\")) continue;\n\t\tif (/^\\s/.test(rawLine)) continue;\n\n\t\tconst match = line.match(/^([A-Za-z0-9_-]+):(.*)$/);\n\t\tif (!match) {\n\t\t\tthrow new Error(\n\t\t\t\t`${filePath} has invalid YAML frontmatter on line ${index + 2}: ${trimmed}`,","sourceCodeStart":2234,"sourceCodeEnd":2270,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/setup.ts#L2234-L2270","documentation":"parseSkillFrontmatter requires the file to begin with a '---' delimited YAML frontmatter block containing at least name and description. If the regex /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/ does not match, the file is rejected before any field parsing.","triggerScenarios":"SKILL.md missing the opening '---' fence, having text before it, using '···' or other delimiters, or missing the closing fence so the block never terminates.","commonSituations":"Writing a plain markdown file and forgetting frontmatter entirely, Windows line-ending mixups, or fences indented by spaces (must start at column 0).","solutions":["Start the file with '---' on line 1, add the fields, close with a second '---'","Ensure both fences begin at column 0 with no leading whitespace","Include non-empty name and description fields inside the block","Normalize line endings (CRLF is tolerated, stray BOM is not — remove BOM)"],"exampleFix":"# before\n# My Skill\nDoes things.\n# after\n---\nname: my-skill\ndescription: Does things.\n---\n# My Skill\nDoes things.","handlingStrategy":"validation","validationCode":"const hasFences = content.startsWith(\"---\") && /^---\\r?\\n[\\s\\S]*?\\r?\\n---/.test(content);\nif (!hasFences) throw new Error(\"missing or misplaced frontmatter fences\");","typeGuard":"const startsWithFrontmatter = (c: string): boolean =>\n  /^---\\r?\\n[\\s\\S]*?\\r?\\n---(?:\\r?\\n|$)/.test(c);","tryCatchPattern":"try { parseSkillFrontmatter(content); } catch (e) { if (e instanceof Error && e.message.includes(\"must start with YAML frontmatter\")) { /* add --- fences at column 0 with name and description */ } else throw e; }","preventionTips":["Start every SKILL.md with a --- fence at column 0","Strip BOM bytes before parsing","Include non-empty name and description in the block"],"tags":["yaml","frontmatter","skill","parsing"],"backgroundTag":"config-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}