{"record":{"id":"a251bb525d6b08df","repo":"Hmbown/CodeWhale","slug":"skill-md-is-missing-the-closing-frontmatter","errorCode":null,"errorMessage":"SKILL.md is missing the closing '---' frontmatter fence","messagePattern":"SKILL\\.md is missing the closing '---' frontmatter fence","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/skills/install.rs","lineNumber":1598,"sourceCode":"        std::borrow::Cow::Owned(rest.to_string())\n    } else if path == prefix {\n        std::borrow::Cow::Borrowed(\"\")\n    } else {\n        std::borrow::Cow::Borrowed(path)\n    }\n}\n\n/// Extract `name:` and ensure `description:` exist in the SKILL.md frontmatter.\n/// Also verifies the leading `---` fence so we reject malformed files early.\nfn parse_frontmatter_name(bytes: &[u8]) -> Result<String> {\n    let content = std::str::from_utf8(bytes).context(\"SKILL.md is not valid UTF-8\")?;\n    let trimmed = content.trim_start();\n    if !trimmed.starts_with(\"---\") {\n        bail!(\"SKILL.md is missing the leading '---' frontmatter fence\");\n    }\n    let after_open = &trimmed[3..];\n    let close = after_open.find(\"---\").ok_or_else(|| {\n        anyhow::anyhow!(\"SKILL.md is missing the closing '---' frontmatter fence\")\n    })?;\n    let frontmatter = &after_open[..close];\n\n    let mut name: Option<String> = None;\n    let mut has_description = false;\n    for raw in frontmatter.lines() {\n        let line = raw.trim();\n        if line.is_empty() || line.starts_with('#') {\n            continue;\n        }\n        if let Some((key, value)) = line.split_once(':') {\n            let key = key.trim().to_ascii_lowercase();\n            let value = value.trim().to_string();\n            match key.as_str() {\n                \"name\" if !value.is_empty() => name = Some(value),\n                \"description\" if !value.is_empty() => has_description = true,\n                _ => {}\n            }","sourceCodeStart":1580,"sourceCodeEnd":1616,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/skills/install.rs#L1580-L1616","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before (SKILL.md)\n---\nname: my-skill\ndescription: Does a thing.\n\n# My Skill\n...\n\n# after\n---\nname: my-skill\ndescription: Does a thing.\n---\n\n# My Skill\n...","handlingStrategy":"validation","validationCode":"fn frontmatter_fences_ok(md: &str) -> bool {\n    let t = md.trim_start();\n    t.starts_with(\"---\") && t[3..].find(\"---\").is_some()\n}\n\nlet md = std::fs::read_to_string(skill_md_path)?;\nanyhow::ensure!(frontmatter_fences_ok(&md), \"SKILL.md frontmatter fences malformed\");","typeGuard":"function hasClosedFrontmatter(md: string): boolean {\n  const t = md.replace(/^\\s+/, '');\n  if (!t.startsWith('---')) return false;\n  return t.slice(3).includes('---');\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["skills","frontmatter","parsing","install","yaml"],"backgroundTag":"yaml-frontmatter-malformed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}