{"record":{"id":"f3baf5246b44de25","repo":"sipeed/picoclaw","slug":"cannot-create-skill-q-skill-already-exists","errorCode":null,"errorMessage":"cannot create skill %q: skill already exists","messagePattern":"cannot create skill %q: skill already exists","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/evolution/apply.go","lineNumber":180,"sourceCode":"\t}\n\tif name != targetSkillName {\n\t\treturn fmt.Errorf(\"skill frontmatter name %q does not match target skill %q\", name, targetSkillName)\n\t}\n\tif strings.TrimSpace(fields[\"description\"]) == \"\" {\n\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)","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/evolution/apply.go#L162-L198","documentation":"renderAppliedBody (pkg/evolution/apply.go:180) refuses change_kind \"create\" when backupCurrentSkill found an existing <workspace>/skills/<name>/SKILL.md. This guards idempotency: a create must never silently overwrite an already-deployed skill. The error is raised before any file write, so there are no side effects.","triggerScenarios":"ApplyDraft with ChangeKindCreate when the target skill file exists — re-running an evolution pass after an earlier accepted create; a manually authored skill with the same name; two evolution runs racing on the same workspace.","commonSituations":"Retrying a pipeline run that already applied; name collisions between generated and hand-written skills; drafts generated while the skill was concurrently created by another process.","solutions":["Change the draft's ChangeKind to replace (overwrite intent) or append/merge (additive intent)","Delete or rename the existing <workspace>/skills/<name>/SKILL.md if a fresh create is truly intended","Pick a different TargetSkillName for the new skill","Filter out create drafts whose target already exists before running apply"],"exampleFix":"// before\ndraft.ChangeKind = evolution.ChangeKindCreate // target skill already on disk\n\n// after (choose one intent)\ndraft.ChangeKind = evolution.ChangeKindReplace  // overwrite\ndraft.ChangeKind = evolution.ChangeKindAppend   // add a section","handlingStrategy":"validation","validationCode":"func skillExists(workspace, name string) bool {\n\t_, err := os.Stat(filepath.Join(workspace, \"skills\", name, \"SKILL.md\"))\n\treturn err == nil\n}\n\nif draft.ChangeKind == evolution.ChangeKindCreate && skillExists(ws, draft.TargetSkillName) {\n\t// choose replace/append, rename, or skip — do not call ApplyDraft","typeGuard":null,"tryCatchPattern":"if err := applier.ApplyDraft(ctx, ws, draft); err != nil {\n\tif strings.Contains(err.Error(), \"skill already exists\") {\n\t\t// flip ChangeKind to replace/append or pick a new TargetSkillName\n\t}\n}","preventionTips":["Decide change kind from on-disk existence at apply time, not at draft generation time","Make create-draft generation skip targets that already exist","Run a single evolution writer per workspace to avoid create/create races"],"tags":["go","skills","state-conflict","idempotency"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}