Hmbown/CodeWhale · error · anyhow::Error
SKILL.md is missing the closing '---' frontmatter fence
Error message
SKILL.md is missing the closing '---' frontmatter fence
What it means
parse_frontmatter_name validates a SKILL.md before install: the file must be valid UTF-8, start with a '---' fence, and contain a second '---' that closes the frontmatter block. This error fires when the opening fence exists but no closing '---' is found anywhere after it, so the frontmatter (name/description) cannot be delimited. The search is a plain find of '---' in the remainder, so the closing fence must appear on its own before the body.
Source
Thrown at crates/tui/src/skills/install.rs:1598
std::borrow::Cow::Owned(rest.to_string())
} else if path == prefix {
std::borrow::Cow::Borrowed("")
} else {
std::borrow::Cow::Borrowed(path)
}
}
/// Extract `name:` and ensure `description:` exist in the SKILL.md frontmatter.
/// Also verifies the leading `---` fence so we reject malformed files early.
fn parse_frontmatter_name(bytes: &[u8]) -> Result<String> {
let content = std::str::from_utf8(bytes).context("SKILL.md is not valid UTF-8")?;
let trimmed = content.trim_start();
if !trimmed.starts_with("---") {
bail!("SKILL.md is missing the leading '---' frontmatter fence");
}
let after_open = &trimmed[3..];
let close = after_open.find("---").ok_or_else(|| {
anyhow::anyhow!("SKILL.md is missing the closing '---' frontmatter fence")
})?;
let frontmatter = &after_open[..close];
let mut name: Option<String> = None;
let mut has_description = false;
for raw in frontmatter.lines() {
let line = raw.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if let Some((key, value)) = line.split_once(':') {
let key = key.trim().to_ascii_lowercase();
let value = value.trim().to_string();
match key.as_str() {
"name" if !value.is_empty() => name = Some(value),
"description" if !value.is_empty() => has_description = true,
_ => {}
}View on GitHub (pinned to 0c42157ee5)
Solutions
- Open SKILL.md and add a line containing only '---' after the YAML frontmatter (after name: and description: lines).
- Verify structure: line 1 '---', frontmatter keys, then a closing '---' line, then the body.
- If the file intentionally has no frontmatter, add a minimal block: '---\nname: my-skill\ndescription: ...\n---'.
- Re-run the install; the fence check happens before any files are copied.
Example fix
# before (SKILL.md) --- name: my-skill description: Does a thing. # My Skill ... # after --- name: my-skill description: Does a thing. --- # My Skill ...
Defensive patterns
Strategy: validation
Validate before calling
fn frontmatter_fences_ok(md: &str) -> bool {
let t = md.trim_start();
t.starts_with("---") && t[3..].find("---").is_some()
}
let md = std::fs::read_to_string(skill_md_path)?;
anyhow::ensure!(frontmatter_fences_ok(&md), "SKILL.md frontmatter fences malformed"); Type guard
function hasClosedFrontmatter(md: string): boolean {
const t = md.replace(/^\s+/, '');
if (!t.startsWith('---')) return false;
return t.slice(3).includes('---');
} Prevention
- Template every new SKILL.md with opening and closing '---' fences from the start.
- Run a fence check in CI or a pre-install lint for skill packages.
- Never let a horizontal-rule '---' be the first line of the file unless real frontmatter follows.
When it happens
Trigger: Installing a skill whose SKILL.md begins with '---' but never closes the frontmatter block — e.g. only one fence at the top, or frontmatter that runs to EOF. The opener check passes (error would otherwise be the 'leading fence' variant), then after_open.find("---") returns None.
Common situations: Hand-authored SKILL.md missing the second fence; a markdown file converted from a template that uses '---' as a horizontal rule; truncated file where the closing fence was cut off.
Related errors
- install source must not be empty
- github source must be 'github:owner/repo' (got {spec})
- installed package failed content digest validation: {err}
- unrecognised character {ch:?} in duration {s:?}
- invalid SHA256 manifest line {}: {trimmed}
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/a251bb525d6b08df.
Report an issue: GitHub.