charmbracelet/crush · error

parsing frontmatter: %w

Error message

parsing frontmatter: %w

What it means

ParseContent splits a SKILL.md document into YAML frontmatter and a body, then unmarshals the frontmatter into a Skill struct. This error wraps any yaml.Unmarshal failure, meaning the frontmatter block is not valid YAML or does not match the Skill schema.

Source

Thrown at internal/skills/skills.go:175

		return nil, err
	}

	skill.Path = filepath.Dir(path)
	skill.SkillFilePath = path

	return skill, nil
}

// ParseContent parses a SKILL.md from raw bytes.
func ParseContent(content []byte) (*Skill, error) {
	frontmatter, body, err := splitFrontmatter(string(content))
	if err != nil {
		return nil, err
	}

	var skill Skill
	if err := yaml.Unmarshal([]byte(frontmatter), &skill); err != nil {
		return nil, fmt.Errorf("parsing frontmatter: %w", err)
	}

	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) != ""

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Validate the YAML between the --- fences with a YAML linter/parser; fix indentation (spaces only, no tabs)
  2. Quote the `description:` and `compatibility:` values (or use block scalars | >) when they contain colons or special characters
  3. Ensure the file starts with `---`, has a closing `---`, and fields match the Skill schema (name, description, etc.)

Example fix

// before
// description: Use when: user asks for tests
// after
// description: "Use when: user asks for tests"
Defensive patterns

Strategy: try-catch

Validate before calling

func hasFrontmatter(content string) bool {
	lines := strings.SplitN(content, "\n", 2)
	if len(lines) == 0 || strings.TrimSpace(lines[0]) != "---" {
		return false
	}
	_, rest, ok := strings.Cut(content[4:], "\n---")
	return ok && strings.TrimSpace(rest) != ""
}

Try / catch

skill, err := skills.ParseContent(content)
if err != nil {
	var skipped *fs.PathError
	if errors.As(err, &skipped) {
		return fmt.Errorf("unreadable skill file: %w", err)
	}
	return fmt.Errorf("invalid SKILL.md (check YAML frontmatter syntax, indentation, and --- fences): %w", err)
}

Prevention

When it happens

Trigger: Calling ParseContent (or Parse) on SKILL.md content whose frontmatter contains malformed YAML: bad indentation, tabs instead of spaces, unquoted special characters (e.g. colons in unquoted values), wrong types for fields, or missing/unclosed `---` delimiters (which can yield empty/invalid frontmatter).

Common situations: Hand-edited SKILL.md with YAML syntax mistakes; description containing unquoted colon-bearing text; copy-pasting frontmatter with tabs; editing with a tool that mangles the `---` fences; passing content with no frontmatter at all.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/2d2ad4a8498fba80. Report an issue: GitHub.