larksuite/cli · error

SKILL.md frontmatter is not closed

Error message

SKILL.md frontmatter is not closed

What it means

parseRequiredSkills scans the leading `---` delimited YAML frontmatter of SKILL.md and throws "SKILL.md frontmatter is not closed" when the opening `---` block never terminates. The library refuses to guess which lines are frontmatter, so an unterminated block makes the skill's dependency declaration unparsable.

Source

Thrown at internal/skillpolicy/dependencies.go:62

	// A UTF-8 text file may begin with one BOM. Normalize it before checking
	// the delimiter so BOM-prefixed dependency metadata cannot be skipped.
	content := strings.TrimPrefix(string(skillMD), "\uFEFF")
	lines := strings.Split(content, "\n")
	if strings.TrimRight(lines[0], "\r") != "---" {
		return nil, nil
	}

	block := make([]string, 0, len(lines))
	closed := false
	for _, line := range lines[1:] {
		if strings.TrimRight(line, "\r") == "---" {
			closed = true
			break
		}
		block = append(block, line)
	}
	if !closed {
		return nil, fmt.Errorf("SKILL.md frontmatter is not closed")
	}

	var frontmatter struct {
		Metadata struct {
			Requires struct {
				Skills []string `yaml:"skills"`
			} `yaml:"requires"`
		} `yaml:"metadata"`
	}
	if err := yaml.Unmarshal([]byte(strings.Join(block, "\n")), &frontmatter); err != nil {
		return nil, fmt.Errorf("cannot parse SKILL.md frontmatter: %w", err)
	}

	required := frontmatter.Metadata.Requires.Skills
	seen := make(map[string]struct{}, len(required))
	out := make([]string, 0, len(required))
	for _, dependency := range required {
		if !isSkillName(dependency) {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Add the closing `---` line immediately after the frontmatter keys, at column 0
  2. Remove any leading whitespace before both `---` delimiters
  3. Run the skill-format check (node scripts/skill-format-check/index.js) to catch malformed frontmatter
  4. Validate the SKILL.md against a known-good skill file's structure

Example fix

// before
---
metadata:
  requires:
    skills: [foo]
   (no closing delimiter)
// after
---
metadata:
  requires:
    skills: [foo]
---
Defensive patterns

Strategy: validation

Validate before calling

func frontmatterClosed(data []byte) bool {
    lines := strings.Split(string(data), "\n")
    if len(lines) == 0 || strings.TrimSpace(lines[0]) != "---" { return false }
    for _, l := range lines[1:] {
        if strings.TrimSpace(l) == "---" { return true }
    }
    return false
}
// if !frontmatterClosed(skillMD) { fix the file }

Try / catch

if _, err := parseRequiredSkills(name, data); err != nil {
    if strings.Contains(err.Error(), "frontmatter is not closed") {
        return fmt.Errorf("skill %s: add the closing '---' line after the frontmatter block", name)
    }
    return err
}

Prevention

When it happens

Trigger: SKILL.md starts with `---` but no matching closing `---` line appears before the scan ends — e.g. the closing delimiter was deleted, indented, or written as `----`/```---` with extra characters.

Common situations: Hand-editing SKILL.md and accidentally removing the second `---`; copy-pasting frontmatter that lost its trailing delimiter; a templating step truncating the file; writing the delimiter with leading whitespace so it is not recognized.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/7fe1b935140fae86. Report an issue: GitHub.