sipeed/picoclaw · error
skill frontmatter name %q does not match target skill %q
Error message
skill frontmatter name %q does not match target skill %q
What it means
Thrown by validateAppliedSkillBody (pkg/evolution/apply.go:164) after a draft is rendered into the final SKILL.md body: the YAML frontmatter `name` value must exactly equal the draft's TargetSkillName, which is also the on-disk directory under <workspace>/skills/. The evolution applier enforces this so a deployed skill file always self-identifies with the folder it lives in and skill loaders/recall never see a name/folder mismatch. For append/merge the existing file's frontmatter is kept verbatim, so it is the existing skill that gets re-validated here.
Source
Thrown at pkg/evolution/apply.go:164
func validateAppliedSkillBody(body, targetSkillName string, allowExtraFrontmatterFields bool) error {
body = strings.TrimSpace(body)
if !strings.HasPrefix(body, "---\n") {
return fmt.Errorf("skill frontmatter is required")
}
if !strings.Contains(body, "\n# ") {
return fmt.Errorf("skill heading is required")
}
frontmatter, _ := splitSkillFrontmatter(body)
fields, err := parseSkillFrontmatterFields(frontmatter, allowExtraFrontmatterFields)
if err != nil {
return err
}
name := strings.TrimSpace(fields["name"])
if name == "" {
return fmt.Errorf("skill frontmatter name is required")
}
if name != targetSkillName {
return fmt.Errorf("skill frontmatter name %q does not match target skill %q", name, targetSkillName)
}
if strings.TrimSpace(fields["description"]) == "" {
return fmt.Errorf("skill frontmatter description is required")
}
return nil
}
func allowsExistingFrontmatterFields(kind ChangeKind, hadOriginal bool) bool {
return hadOriginal && (kind == ChangeKindAppend || kind == ChangeKindMerge)
}
func renderAppliedBody(draft SkillDraft, existingBody string, hadOriginal bool) (string, error) {
switch draft.ChangeKind {
case ChangeKindCreate:
if hadOriginal {
return "", fmt.Errorf("cannot create skill %q: skill already exists", draft.TargetSkillName)
}
return renderDeployableSkillBody(draft.BodyOrPatch), nilView on GitHub (pinned to 49183d7e8d)
Solutions
- Make the frontmatter `name` field exactly equal draft.TargetSkillName (same case, hyphen-separated, <=64 chars)
- For append/merge onto a hand-edited skill, fix the existing SKILL.md frontmatter name to match its directory name before applying
- If the new frontmatter name is the intended one, change draft.TargetSkillName (and the target directory) instead of the body
- Regenerate the draft body with the name pinned to the target skill
Example fix
// before (draft) frontmatter: "---\nname: commit_helper\ndescription: ...\n---\n# ..." TargetSkillName: "commit-helper" // after frontmatter: "---\nname: commit-helper\ndescription: ...\n---\n# ..." TargetSkillName: "commit-helper"
Defensive patterns
Strategy: validation
Validate before calling
func frontmatterNameMatches(body, target string) error {
lines := strings.Split(strings.TrimSpace(body), "\n")
if len(lines) < 2 || strings.TrimSpace(lines[0]) != "---" {
return nil // missing frontmatter fails earlier checks
}
end := -1
for i := 1; i < len(lines); i++ {
if strings.TrimSpace(lines[i]) == "---" {
end = i
break
}
}
if end < 0 {
return nil
}
var fm struct {
Name string `yaml:"name"`
}
if err := yaml.Unmarshal([]byte(strings.Join(lines[1:end], "\n")), &fm); err != nil {
return fmt.Errorf("invalid frontmatter: %w", err)
}
if strings.TrimSpace(fm.Name) != target {
return fmt.Errorf("frontmatter name %q != target %q", fm.Name, target)
}
return nil
}
// before apply:
// if err := frontmatterNameMatches(draft.BodyOrPatch, draft.TargetSkillName); err != nil { ... } Try / catch
if err := applier.ApplyDraft(ctx, ws, draft); err != nil {
if strings.Contains(err.Error(), "does not match target skill") {
// fix draft frontmatter name or TargetSkillName, not the store
}
} Prevention
- Pin the frontmatter name to draft.TargetSkillName in the draft generator prompt and in any hand-authored draft
- Before enabling evolution on a workspace, lint every skills/<dir>/SKILL.md so frontmatter name == directory name (append/merge re-validates the existing file)
- Normalize skill slugs once (lowercase, hyphens) at draft creation so generator output and target names cannot drift
When it happens
Trigger: ApplyDraft / runtime cold-path apply with change_kind create or replace where draft.BodyOrPatch frontmatter says `name: commit_helper` while draft.TargetSkillName is `commit-helper` (case, underscores vs hyphens, renamed concept); or change_kind append/merge onto an existing <workspace>/skills/<name>/SKILL.md whose frontmatter name was hand-edited away from its directory name.
Common situations: LLM draft generators producing a slug different from the target_skill_name set by the organizer; a developer renaming a skill directory without editing SKILL.md frontmatter or vice versa; re-applying an old draft after the target skill was renamed; copy-pasting frontmatter from another skill file.
Related errors
- skill frontmatter description is required
- skill frontmatter name is required
- skill patch frontmatter name %q does not match target skill
- invalid skill frontmatter: %w
- unsupported skill frontmatter field %q
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/39285d4aae9ce9d9.
Report an issue: GitHub.