charmbracelet/crush · error
no YAML frontmatter found
Error message
no YAML frontmatter found
What it means
splitFrontmatter splits a SKILL.md into YAML frontmatter and body. It throws this error when the first non-blank line of the content is not exactly `---`, meaning there is no YAML frontmatter block to parse.
Source
Thrown at internal/skills/skills.go:196
skill.Instructions = strings.TrimSpace(body)
return &skill, nil
}
// splitFrontmatter extracts YAML frontmatter and body from markdown content.
func splitFrontmatter(content string) (frontmatter, body string, err error) {
// Strip UTF-8 BOM for compatibility with editors that include it.
content = strings.TrimPrefix(content, "\uFEFF")
// Normalize line endings to \n for consistent parsing.
content = strings.ReplaceAll(content, "\r\n", "\n")
content = strings.ReplaceAll(content, "\r", "\n")
lines := strings.Split(content, "\n")
start := slices.IndexFunc(lines, func(line string) bool {
return strings.TrimSpace(line) != ""
})
if start == -1 || strings.TrimSpace(lines[start]) != "---" {
return "", "", errors.New("no YAML frontmatter found")
}
endOffset := slices.IndexFunc(lines[start+1:], func(line string) bool {
return strings.TrimSpace(line) == "---"
})
if endOffset == -1 {
return "", "", errors.New("unclosed frontmatter")
}
end := start + 1 + endOffset
frontmatter = strings.Join(lines[start+1:end], "\n")
body = strings.Join(lines[end+1:], "\n")
return frontmatter, body, nil
}
// Discover finds all valid skills in the given paths.
func Discover(paths []string) []*Skill {
skills, _ := DiscoverWithStates(paths)View on GitHub (pinned to 7944b8e522)
Solutions
- Start the SKILL.md with a line containing exactly `---`
- Remove any BOM or leading blank/whitespace-prefixed characters so `---` is the first non-blank line
- Ensure the delimiter line has no trailing spaces or extra dashes
Example fix
// before (SKILL.md) # My skill --- name: my-skill --- // after --- name: my-skill description: Does things --- # My skill
Defensive patterns
Strategy: validation
Validate before calling
func hasFrontmatter(content string) bool {
for _, line := range strings.Split(content, "\n") {
if strings.TrimSpace(line) == "" { continue }
return strings.TrimSpace(line) == "---"
}
return false
} Try / catch
fm, body, err := skills.ParseContent(content)
if err != nil {
return fmt.Errorf("malformed skill file (frontmatter): %w", err)
} Prevention
- Start every SKILL.md with a `---` line on line 1
- Save files without BOM
- Never put prose above the frontmatter block
When it happens
Trigger: Calling ParseContent on a file that starts with prose/markdown instead of a `---` delimited block; a BOM or stray characters before the opening `---`; frontmatter delimiters written as `----` or with trailing content on the line.
Common situations: Plain markdown files placed in a skills directory; files saved with a UTF-8 BOM; accidentally deleting the opening `---` while editing.
Related errors
- unclosed frontmatter
- parsing frontmatter: %w
- name is required
- description is required
- name exceeds %d characters
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/5b20204f6d52e7d2.
Report an issue: GitHub.