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), nil

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Make the frontmatter `name` field exactly equal draft.TargetSkillName (same case, hyphen-separated, <=64 chars)
  2. For append/merge onto a hand-edited skill, fix the existing SKILL.md frontmatter name to match its directory name before applying
  3. If the new frontmatter name is the intended one, change draft.TargetSkillName (and the target directory) instead of the body
  4. 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

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


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/39285d4aae9ce9d9. Report an issue: GitHub.