zeroclaw-labs/zeroclaw · error · anyhow::Error
Skill content is missing YAML front-matter (expected `---` d
Error message
Skill content is missing YAML front-matter (expected `---` delimited block at top)
What it means
validate_skill_content splits SKILL.md on a --- delimited YAML front-matter block via split_front_matter, which requires the file to start with a literal '---\n' (after CRLF normalization) and to contain a closing '---' on its own line. This error means no such block was found, so there is nowhere to read the required name field from.
Source
Thrown at crates/zeroclaw-runtime/src/skills/improver.rs:140
self.cooldowns.insert(slug.to_string(), Instant::now());
Ok(Some(slug.to_string()))
}
fn skills_dir(&self) -> PathBuf {
self.workspace_dir.join("skills")
}
}
/// Validate skill content: must be non-empty, have a parseable YAML front-matter
/// block with a non-empty `name` field.
pub fn validate_skill_content(content: &str) -> Result<()> {
if content.trim().is_empty() {
bail!("Skill content is empty");
}
let Some((front, _body)) = split_front_matter(content) else {
bail!("Skill content is missing YAML front-matter (expected `---` delimited block at top)");
};
let name = front_matter_value(&front, "name").unwrap_or_default();
if name.trim().is_empty() {
bail!("Skill front-matter missing required `name` field");
}
Ok(())
}
/// Split a SKILL.md into (front_matter_text, body_text).
/// Returns `None` if the file doesn't start with `---\n` or has no closing
/// `---` delimiter.
fn split_front_matter(content: &str) -> Option<(String, String)> {
let normalized = content.replace("\r\n", "\n");
let rest = normalized.strip_prefix("---\n")?;
if let Some(idx) = rest.find("\n---\n") {
Some((rest[..idx].to_string(), rest[idx + 5..].to_string()))View on GitHub (pinned to 88bb9c8533)
Solutions
- Prepend a block that starts the file: ---\nname: <slug>\n---\n followed by the body
- Remove any leading blank lines, comments, or BOM bytes before the opening ---
- Make sure the closing --- sits on its own line with a newline after the last front-matter key
- Pre-validate with the public validate_skill_content before calling improve_skill
Example fix
# before (body only) Here is how to deploy... # after --- name: deploy-guide --- Here is how to deploy...
Defensive patterns
Strategy: validation
Validate before calling
// Reuse the library's own public validator before the write path: zeroclaw_runtime::skills::validate_skill_content(&improved_content)?; improver.improve_skill(slug, improved_content, reason).await?;
Type guard
fn has_yaml_front_matter(content: &str) -> bool {
let n = content.replace("\r\n", "\n");
match n.strip_prefix("---\n") {
Some(rest) => rest.contains("\n---\n") || rest.ends_with("\n---"),
None => false,
}
} Try / catch
match improve_skill(...).await {
Err(e) if e.to_string().contains("missing YAML front-matter") => {
// regenerate with the original front-matter preserved, then retry once
}
r => r,
} Prevention
- When generating improvements, seed the prompt with the existing front-matter and require it verbatim in the output
- Strip BOMs and leading blank lines before validation
- Validate content shape before it ever reaches the improver
When it happens
Trigger: Passing markdown body with no front-matter; front-matter not at byte 0 (leading blank line, comment, or UTF-8 BOM); missing closing '---'; an opening '---' immediately followed by text on the same line instead of a newline.
Common situations: LLM-generated improvements that rewrite the file and drop the front-matter; hand-edited SKILL.md files; content assembled by concatenation that puts prose before the front-matter block; editors saving with a BOM.
Related errors
- Skill front-matter missing required `name` field
- Skill content is empty
- Skill source must be a directory: {}
- invalid --skill name '{$skill}': use a bare skill name (lett
- modal custom_id exceeds Discord's 100-char limit; cannot ope
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/379e4a00b9c555f5.
Report an issue: GitHub.