sipeed/picoclaw · error
resolve lifecycle delete skill name: %w
Error message
resolve lifecycle delete skill name: %w
What it means
ApplyLifecycleState (pkg/evolution/lifecycle.go:56) runs skills.ValidateSkillName before deleting: the name must be non-empty, not an absolute path, at most 64 characters, and match ^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$ — no underscores, spaces, slashes, or dots. The underlying reason is wrapped with %w. This prevents path traversal or malformed deletes from corrupt profile records.
Source
Thrown at pkg/evolution/lifecycle.go:56
}
return profile.Status
}
func ApplyLifecycleState(paths Paths, profile SkillProfile, next SkillStatus) error {
if next != SkillStatusDeleted {
return nil
}
workspace := profile.WorkspaceID
if workspace == "" {
workspace = inferWorkspaceFromPaths(paths)
}
if workspace == "" {
return fmt.Errorf("resolve lifecycle delete workspace for skill %q: workspace is required", profile.SkillName)
}
if err := skills.ValidateSkillName(profile.SkillName); err != nil {
return fmt.Errorf("resolve lifecycle delete skill name: %w", err)
}
skillPath := filepath.Join(workspace, "skills", profile.SkillName, "SKILL.md")
err := os.Remove(skillPath)
if errors.Is(err, os.ErrNotExist) {
return nil
}
return err
}
func RunLifecycleOnce(store *Store, paths Paths, workspace string, now time.Time) (LifecycleRunSummary, error) {
if store == nil {
return LifecycleRunSummary{}, nil
}
profiles, err := store.LoadProfiles()
if err != nil {
return LifecycleRunSummary{}, errView on GitHub (pinned to 49183d7e8d)
Solutions
- Fix the profile's skill_name to the slug rules (lowercase alphanumerics and single hyphens, <=64 chars) to match the real on-disk skill directory
- Delete the corrupt/stale profile record so it stops blocking lifecycle runs
- Backfill and normalize skill names during migration
Example fix
// before profile.SkillName = "my_skill" // rejected: underscore // after profile.SkillName = "my-skill" // matches skills/my-skill/SKILL.md
Defensive patterns
Strategy: validation
Validate before calling
if err := skills.ValidateSkillName(profile.SkillName); err != nil {
// fix or drop the profile before running the lifecycle
} Type guard
func isValidSkillName(name string) bool {
return name != "" && !filepath.IsAbs(name) && len(name) <= 64 &&
regexp.MustCompile(`^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$`).MatchString(name)
} Try / catch
if err := evolution.ApplyLifecycleState(paths, profile, evolution.SkillStatusDeleted); err != nil {
if strings.Contains(err.Error(), "resolve lifecycle delete skill name") {
// normalize profile.SkillName to the slug rules matching the on-disk directory
}
} Prevention
- Create skills only through paths that enforce the slug pattern (alphanumerics + single hyphens, <=64 chars, no underscores/spaces/slashes)
- Validate profile skill_name when loading profiles from external sources
- Monitor profile stores for empty/corrupt skill_name fields
When it happens
Trigger: A stored SkillProfile with skill_name like `my_skill`, `My Skill`, `team/repo-skill`, an absolute path, a >64-char name, or an empty skill_name from truncated/corrupt profile JSON reaching the deleted transition.
Common situations: Legacy profiles created before strict naming; profiles hand-imported from another tool; partially written profile files; workspace migrations renaming skills outside the slug rules.
Related errors
- skill frontmatter name %q does not match target skill %q
- skill frontmatter description is required
- cannot replace skill %q: skill does not exist
- skill patch frontmatter name %q does not match target skill
- err.Error() [dynamic from importUploadedSkill]
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/60c302880e613b71.
Report an issue: GitHub.