{"record":{"id":"1522709601aa6328","repo":"sipeed/picoclaw","slug":"failed-to-remove-skill-s-w","errorCode":null,"errorMessage":"failed to remove skill '%s': %w","messagePattern":"failed to remove skill '(.+?)': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/skills/helpers.go","lineNumber":177,"sourceCode":"\tif name == \"\" {\n\t\treturn fmt.Errorf(\"skill name is required\")\n\t}\n\tif strings.Contains(name, \"/\") {\n\t\tdirName, err := skills.GitHubInstallDirNameFromToolsConfig(toolsConfig, name)\n\t\tif err != nil || dirName == \"\" {\n\t\t\treturn fmt.Errorf(\"invalid skill name %q\", skillName)\n\t\t}\n\t\tname = dirName\n\t}\n\tif name == \".\" || name == \"..\" {\n\t\treturn fmt.Errorf(\"invalid skill name %q\", skillName)\n\t}\n\tskillDir := filepath.Join(workspace, \"skills\", name)\n\tif _, err := os.Stat(skillDir); os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"skill '%s' not found\", name)\n\t}\n\tif err := os.RemoveAll(skillDir); err != nil {\n\t\treturn fmt.Errorf(\"failed to remove skill '%s': %w\", name, err)\n\t}\n\treturn nil\n}\n\nfunc skillsInstallBuiltinCmd(workspace string) {\n\tbuiltinSkillsDir := \"./picoclaw/skills\"\n\tworkspaceSkillsDir := filepath.Join(workspace, \"skills\")\n\n\tfmt.Printf(\"Copying builtin skills to workspace...\\n\")\n\n\tskillsToInstall := []string{\n\t\t\"weather\",\n\t\t\"news\",\n\t\t\"stock\",\n\t\t\"calculator\",\n\t}\n\n\tfor _, skillName := range skillsToInstall {","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/skills/helpers.go#L159-L195","documentation":"os.RemoveAll on <workspace>/skills/<name> returned a non-nil error, wrapped with the skill name and the underlying cause. This is an OS-level failure during deletion (permissions, read-only filesystem, open file handles), not a validation problem — the directory existed but could not be removed.","triggerScenarios":"Removing a skill whose directory contains files the process cannot write/delete (ownership mismatch, mode bits), a read-only or full disk mount, or (on Windows) a file inside the skill dir held open by an editor, indexer, or antivirus.","commonSituations":"Workspace created by root/admin but CLI run as a normal user; workspace on a network or read-only mount; a file watcher or IDE holding SKILL.md open; NFS stale-handle errors.","solutions":["Inspect the wrapped %w cause: EACCES/EPERM means permissions, EROFS means read-only mount","Fix ownership/permissions on the skill directory (e.g. chown -R or chmod -R u+w <workspace>/skills/<name>) and retry","Close editors/IDEs/indexers that may hold files inside the skill directory, then retry","On Windows, retry after stopping antivirus/indexing locks or reboot if a handle is stuck"],"exampleFix":"# inspect the cause\npicoclaw skills remove weather\n# -> failed to remove skill 'weather': ... permission denied\n\n# fix and retry\nchmod -R u+w ~/.picoclaw/skills/weather && picoclaw skills remove weather","handlingStrategy":"retry","validationCode":"func canRemove(dir string) bool {\n    info, err := os.Stat(dir)\n    return err == nil && info.IsDir() && info.Mode().Perm()&0200 != 0\n}","typeGuard":null,"tryCatchPattern":"err := skillsRemoveFromWorkspace(ws, cfg, name)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to remove skill\") {\n        // inspect errors.Unwrap(err) for EACCES/EROFS; remediate perms or close locks, then retry once\n    }\n}","preventionTips":["Run the CLI as the user who owns the workspace","Keep workspace skills on a writable local filesystem","Close editors/watchers that lock files inside skill directories"],"tags":["filesystem","permissions","skills","cli"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}