zylon-ai/private-gpt · error · SkillDomainError

MISSING_FRONTMATTER

MISSING_FRONTMATTER

Error message

SKILL.md must start with YAML frontmatter

What it means

parse_skill_markdown expects the SKILL.md content to begin with a YAML frontmatter block (delimited --- fences) matched by _FRONTMATTER_RE; when the regex does not match at position 0 it raises SkillDomainError with code MISSING_FRONTMATTER. This means the parser found no opening fence/structure at the very start of the document — frontmatter must literally be the first thing in the file.

Source

Thrown at private_gpt/components/skills/parser.py:78

    @field_validator("allowed_tools_raw", mode="before")
    @classmethod
    def normalize_list_or_str(cls, value: str | list[str] | None) -> str | None:
        if value is None:
            return None
        if isinstance(value, list):
            return " ".join(str(item).strip() for item in value if str(item).strip())
        return value


class ParsedSkillDocument(BaseModel):
    frontmatter: SkillFrontmatter
    body: str = Field(default="")


def parse_skill_markdown(skill_markdown: str) -> ParsedSkillDocument:
    match = _FRONTMATTER_RE.match(skill_markdown)
    if not match:
        raise SkillDomainError(
            SkillErrorCode.MISSING_FRONTMATTER,
            "SKILL.md must start with YAML frontmatter",
        )

    raw_frontmatter = match.group(1)
    try:
        parsed_yaml = yaml.safe_load(raw_frontmatter)
    except yaml.YAMLError as e:
        raise SkillDomainError(
            SkillErrorCode.INVALID_FRONTMATTER,
            "The SKILL.md frontmatter is not valid YAML.",
        ) from e
    if not isinstance(parsed_yaml, dict):
        raise SkillDomainError(
            SkillErrorCode.INVALID_FRONTMATTER,
            "Invalid SKILL.md frontmatter",
        )

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Make '---' the absolute first line, followed by YAML, closed by a second '---', then the body.
  2. Strip any leading whitespace/blank lines or BOM before parsing (`content.lstrip('\ufeff').lstrip()`).
  3. Ensure you pass the SKILL.md content, not another markdown file.
  4. Add an authoring-time lint that checks the file starts with '---'.

Example fix

# before
# My Skill
---
name: my-skill
---
Body...

# after
---
name: my-skill
---
# My Skill
Body...
Defensive patterns

Strategy: validation

Validate before calling

def has_frontmatter(content: str) -> bool:
    return bool(_FRONTMATTER_RE.match(content)) or content.startswith("---\n")

def normalize(content: str) -> str:
    return content.lstrip("\ufeff").lstrip()

Try / catch

try:
    parsed = parse_skill_markdown(content)
except SkillDomainError as e:
    if e.code is SkillErrorCode.MISSING_FRONTMATTER:
        raise ValueError("SKILL.md must begin with a '---' YAML block; got bare markdown") from e
    raise

Prevention

When it happens

Trigger: Calling parse_skill_markdown on a string that does not start with '---\n' frontmatter — e.g. a README-style markdown file, a SKILL.md beginning with prose or a BOM, or a file whose fences use '···' or are indented.

Common situations: Authors writing a title/intro line before the frontmatter; UTF-8 BOM prepended by Windows editors; using alternate fence styles or leaving a blank first line; passing the wrong file's content into the parser.

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/c81c75e3afa34156. Report an issue: GitHub.