{"record":{"id":"de062a17ea257b3e","repo":"sipeed/picoclaw","slug":"cannot-replace-skill-q-skill-does-not-exist","errorCode":null,"errorMessage":"cannot replace skill %q: skill does not exist","messagePattern":"cannot replace skill %q: skill does not exist","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/evolution/apply.go","lineNumber":185,"sourceCode":"\t\treturn fmt.Errorf(\"skill frontmatter description is required\")\n\t}\n\treturn nil\n}\n\nfunc allowsExistingFrontmatterFields(kind ChangeKind, hadOriginal bool) bool {\n\treturn hadOriginal && (kind == ChangeKindAppend || kind == ChangeKindMerge)\n}\n\nfunc renderAppliedBody(draft SkillDraft, existingBody string, hadOriginal bool) (string, error) {\n\tswitch draft.ChangeKind {\n\tcase ChangeKindCreate:\n\t\tif hadOriginal {\n\t\t\treturn \"\", fmt.Errorf(\"cannot create skill %q: skill already exists\", draft.TargetSkillName)\n\t\t}\n\t\treturn renderDeployableSkillBody(draft.BodyOrPatch), nil\n\tcase ChangeKindReplace:\n\t\tif !hadOriginal {\n\t\t\treturn \"\", fmt.Errorf(\"cannot replace skill %q: skill does not exist\", draft.TargetSkillName)\n\t\t}\n\t\treturn renderDeployableSkillBody(draft.BodyOrPatch), nil\n\tcase ChangeKindAppend:\n\t\tpatch, err := renderDeployablePatchBody(draft.BodyOrPatch, draft.TargetSkillName)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif !hadOriginal || strings.TrimSpace(existingBody) == \"\" {\n\t\t\treturn renderDeployableSkillBody(draft.BodyOrPatch), nil\n\t\t}\n\t\treturn strings.TrimRight(existingBody, \"\\n\") + \"\\n\\n\" + strings.TrimLeft(patch, \"\\n\"), nil\n\tcase ChangeKindMerge:\n\t\tpatch, err := renderDeployablePatchBody(draft.BodyOrPatch, draft.TargetSkillName)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif !hadOriginal || strings.TrimSpace(existingBody) == \"\" {\n\t\t\treturn renderDeployableSkillBody(draft.BodyOrPatch), nil","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/evolution/apply.go#L167-L203","documentation":"renderAppliedBody (pkg/evolution/apply.go:185) refuses change_kind \"replace\" when the target skill does not exist on disk (backupCurrentSkill returned hadOriginal=false). Replace is only defined for an existing skill because it must back up the current body first. No side effects occur — the error precedes any write.","triggerScenarios":"ApplyDraft with ChangeKindReplace on a fresh workspace where <workspace>/skills/<name>/SKILL.md was never created; the target was deleted manually or by the lifecycle (archived->deleted) while the replace draft was pending; TargetSkillName typo pointing at a directory that never existed.","commonSituations":"Workspace reset or skills directory wiped between draft generation and apply; lifecycle garbage collection removing skills with pending drafts; renamed skills invalidating stored drafts.","solutions":["Switch the draft's ChangeKind to create if the skill should be (re)created from scratch","Restore the skill from the newest backup under paths.BackupsDir, then re-apply the replace","Fix TargetSkillName to point at an existing skill"],"exampleFix":"// before\ndraft.ChangeKind = evolution.ChangeKindReplace // skill missing on disk\n\n// after\ndraft.ChangeKind = evolution.ChangeKindCreate","handlingStrategy":"validation","validationCode":"if draft.ChangeKind == evolution.ChangeKindReplace {\n\tif _, err := os.Stat(filepath.Join(ws, \"skills\", draft.TargetSkillName, \"SKILL.md\")); os.IsNotExist(err) {\n\t\t// switch to ChangeKindCreate or restore the skill from paths.BackupsDir first\n\t}\n}","typeGuard":null,"tryCatchPattern":"if err := applier.ApplyDraft(ctx, ws, draft); err != nil {\n\tif strings.Contains(err.Error(), \"skill does not exist\") {\n\t\t// use ChangeKindCreate, or restore the target skill, then retry\n\t}\n}","preventionTips":["Re-check target existence immediately before applying replace drafts — targets get deleted by lifecycle or coworkers","Keep backups under paths.BackupsDir so a deleted target can be restored for a pending replace","Expire or re-validate stale drafts after workspace resets"],"tags":["go","skills","state-conflict","lifecycle"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}