sipeed/picoclaw · warning
✗ skill '%s' already installed at %s
Error message
✗ skill '%s' already installed at %s
What it means
Install was refused because <workspace>/skills/<dirName> already exists — os.Stat succeeded. This is an idempotency guard that fires before download; nothing is modified and the existing install stays intact.
Source
Thrown at cmd/picoclaw/internal/skills/helpers.go:75
registryMgr := skills.NewRegistryManagerFromToolsConfig(cfg.Tools.Skills)
registry := registryMgr.GetRegistry(registryName)
if registry == nil {
return fmt.Errorf("✗ registry '%s' not found or not enabled. check your config.json.", registryName)
}
dirName, err := registry.ResolveInstallDirName(target)
if err != nil {
return fmt.Errorf("✗ invalid install target %q: %w", target, err)
}
fmt.Printf("Installing skill '%s' from %s registry...\n", target, registryName)
workspace := cfg.WorkspacePath()
targetDir := filepath.Join(workspace, "skills", dirName)
if _, err = os.Stat(targetDir); err == nil {
return fmt.Errorf("\u2717 skill '%s' already installed at %s", dirName, targetDir)
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
if err = os.MkdirAll(filepath.Join(workspace, "skills"), 0o755); err != nil {
return fmt.Errorf("\u2717 failed to create skills directory: %w", err)
}
result, err := registry.DownloadAndInstall(ctx, target, "", targetDir)
if err != nil {
rmErr := os.RemoveAll(targetDir)
if rmErr != nil {
fmt.Printf("\u2717 Failed to remove partial install: %v\n", rmErr)
}
return fmt.Errorf("✗ failed to install skill: %w", err)
}
View on GitHub (pinned to 49183d7e8d)
Solutions
- Keep the existing install if it is already what you wanted.
- Otherwise remove it first: `picoclaw skills remove <name>`, then reinstall.
- If the directory is a corrupt leftover, delete <workspace>/skills/<dirName> manually and reinstall.
- Check `picoclaw skills list` to confirm what is installed before retrying.
Example fix
# before picoclaw skills install clawhub/myskill # dir already exists # after picoclaw skills remove myskill && picoclaw skills install clawhub/myskill
Defensive patterns
Strategy: validation
Validate before calling
targetDir := filepath.Join(workspace, "skills", dirName)
if _, err := os.Stat(targetDir); err == nil {
return nil // already installed — treat as success (idempotent) or prompt
} Type guard
func skillInstalled(workspace, dirName string) bool {
_, err := os.Stat(filepath.Join(workspace, "skills", dirName))
return err == nil
} Prevention
- Run `skills list` before scripted installs.
- Make install scripts idempotent by checking for the directory first.
- Track installed names in one place so reinstall flows remove-then-install deliberately.
When it happens
Trigger: Re-running `skills install` for a skill whose resolved directory already exists under the workspace's skills folder (helpers.go:75), regardless of which registry or version installed it.
Common situations: Re-running an install after an interrupted session, two registries resolving to the same dirName, or a leftover directory from a manual copy.
Related errors
- ✗ invalid install target %q: %w
- ✗ failed to create skills directory: %w
- ✗ failed to persist skill metadata: %w
- invalid skill name %q
- create output dir: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/6cb099531ccc982b.
Report an issue: GitHub.