{"record":{"id":"e0ce79a125a136bb","repo":"sipeed/picoclaw","slug":"invalid-install-target-q-w","errorCode":null,"errorMessage":"✗  invalid install target %q: %w","messagePattern":"✗  invalid install target %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/skills/helpers.go","lineNumber":66,"sourceCode":"}\n\n// skillsInstallFromRegistry installs a skill from a named registry (e.g. clawhub).\nfunc skillsInstallFromRegistry(cfg *config.Config, registryName, target string) error {\n\terr := utils.ValidateSkillIdentifier(registryName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"✗  invalid registry name: %w\", err)\n\t}\n\n\tregistryMgr := skills.NewRegistryManagerFromToolsConfig(cfg.Tools.Skills)\n\n\tregistry := registryMgr.GetRegistry(registryName)\n\tif registry == nil {\n\t\treturn fmt.Errorf(\"✗  registry '%s' not found or not enabled. check your config.json.\", registryName)\n\t}\n\n\tdirName, err := registry.ResolveInstallDirName(target)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"✗  invalid install target %q: %w\", target, err)\n\t}\n\n\tfmt.Printf(\"Installing skill '%s' from %s registry...\\n\", target, registryName)\n\n\tworkspace := cfg.WorkspacePath()\n\ttargetDir := filepath.Join(workspace, \"skills\", dirName)\n\n\tif _, err = os.Stat(targetDir); err == nil {\n\t\treturn fmt.Errorf(\"\\u2717 skill '%s' already installed at %s\", dirName, targetDir)\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)\n\tdefer cancel()\n\n\tif err = os.MkdirAll(filepath.Join(workspace, \"skills\"), 0o755); err != nil {\n\t\treturn fmt.Errorf(\"\\u2717 failed to create skills directory: %w\", err)\n\t}\n","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/skills/helpers.go#L48-L84","documentation":"The install target (the part after the registry name) was rejected by registry.ResolveInstallDirName, which normalizes a slug or owner/repo form into an on-disk directory name under <workspace>/skills. Empty, malformed, or unmappable targets fail here, before any download starts.","triggerScenarios":"Targets like \"owner/\" (empty repo), \"/repo\" (empty owner), strings with unexpected path separators or invalid characters, or anything ResolveInstallDirName cannot map — helpers.go:66.","commonSituations":"Copy-pasting a URL fragment instead of the slug, a trailing slash left by shell tab-completion, or a registry whose expected target syntax differs from what was typed.","solutions":["Use the exact target form the registry expects (slug or owner/repo) per its docs.","Strip trailing slashes and stray quotes.","Read the wrapped error for the specific rule broken.","Confirm the target exists on the registry before installing."],"exampleFix":"# before\npicoclaw skills install clawhub/owner/\n# after\npicoclaw skills install clawhub/owner/repo","handlingStrategy":"validation","validationCode":"target = strings.Trim(target, \"/\")\nif _, err := registry.ResolveInstallDirName(target); err != nil {\n    return fmt.Errorf(\"invalid install target %q: %w\", target, err)\n}","typeGuard":"func validInstallTarget(r skills.Registry, t string) bool {\n    _, err := r.ResolveInstallDirName(t)\n    return err == nil\n}","tryCatchPattern":null,"preventionTips":["Trim trailing slashes and reject empty path segments before calling install.","Document the target syntax (slug vs owner/repo) in command help.","Validate against the same rules the registry uses when building install scripts."],"tags":["validation","registry","install","cli","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}