sipeed/picoclaw · error
unsupported skill frontmatter field %q
Error message
unsupported skill frontmatter field %q
What it means
parseSkillFrontmatterFields (pkg/evolution/apply.go:267) rejects frontmatter keys other than name/description when allowExtraFields is false. allowExtraFields comes from allowsExistingFrontmatterFields: true only when the target skill already exists on disk AND change_kind is append or merge. So create and replace always require exactly name+description, and append/merge to a not-yet-existing skill do too.
Source
Thrown at pkg/evolution/apply.go:267
}
}
if end < 0 {
return "", body
}
return strings.Join(lines[1:end], "\n"), strings.TrimLeft(strings.Join(lines[end+1:], "\n"), "\n")
}
func parseSkillFrontmatterFields(frontmatter string, allowExtraFields bool) (map[string]string, error) {
var raw map[string]any
if err := yaml.Unmarshal([]byte(frontmatter), &raw); err != nil {
return nil, fmt.Errorf("invalid skill frontmatter: %w", err)
}
for key := range raw {
if key != "name" && key != "description" {
if allowExtraFields {
continue
}
return nil, fmt.Errorf("unsupported skill frontmatter field %q", key)
}
}
var typed struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
}
if err := yaml.Unmarshal([]byte(frontmatter), &typed); err != nil {
return nil, fmt.Errorf("invalid skill frontmatter: %w", err)
}
return map[string]string{
"name": typed.Name,
"description": typed.Description,
}, nil
}
func stripLeadingH1(body string) string {
lines := strings.Split(strings.TrimLeft(body, "\n"), "\n")View on GitHub (pinned to 49183d7e8d)
Solutions
- Strip the draft frontmatter down to only name and description
- Use append/merge against the existing skill — extra keys already in the existing file's frontmatter are then allowed
- Store extra metadata outside SKILL.md (e.g. in the evolution profile) instead of frontmatter
Example fix
# before (create draft) --- name: deploy-checks description: ... version: 2 metadata: owner: platform --- # after --- name: deploy-checks description: ... ---
Defensive patterns
Strategy: validation
Validate before calling
func frontmatterKeysAllowed(body string, allowExtra bool) error {
var raw map[string]any
if err := yaml.Unmarshal([]byte(extractFrontmatter(body)), &raw); err != nil {
return err
}
for k := range raw {
if k != "name" && k != "description" && !allowExtra {
return fmt.Errorf("unsupported frontmatter field %q", k)
}
}
return nil
}
// allowExtra mirrors allowsExistingFrontmatterFields:
// hadOriginal && (kind == append || kind == merge) Try / catch
if err := applier.ApplyDraft(ctx, ws, draft); err != nil {
if strings.Contains(err.Error(), "unsupported skill frontmatter field") {
// strip the named key, or switch to append/merge onto the existing skill
}
} Prevention
- Remember the contract: create/replace bodies may contain ONLY name and description; extra keys are tolerated only in an existing skill's frontmatter during append/merge
- Do not copy richer frontmatter formats (e.g. metadata-bearing skill schemas) into create/replace drafts
- Store auxiliary metadata in the evolution profile, not in SKILL.md frontmatter
When it happens
Trigger: Create/replace draft with extra keys such as `version:`, `metadata:`, `tags:`, `license:`; append draft where the target skill does not exist (hadOriginal=false); replace draft — the existing skill's richer frontmatter is discarded and the new body must be minimal.
Common situations: LLM adding metadata fields beyond the prompt's two-key contract; authors copying frontmatter from other agent-skill formats that allow `metadata:`; trying to upgrade a hand-written skill with extra fields via a replace draft.
Related errors
- skill frontmatter name %q does not match target skill %q
- skill frontmatter description is required
- model_name must be a string, got %T
- model_name is required: %#v
- failed to parse security config %s: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/3a6a6b94fdb1bf0e.
Report an issue: GitHub.