{"record":{"id":"c8fdcb94eb7d517c","repo":"sipeed/picoclaw","slug":"failed-to-install-skill-w","errorCode":null,"errorMessage":"✗ failed to install skill: %w","messagePattern":"✗ failed to install skill: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/skills/helpers.go","lineNumber":91,"sourceCode":"\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\n\tresult, err := registry.DownloadAndInstall(ctx, target, \"\", targetDir)\n\tif err != nil {\n\t\trmErr := os.RemoveAll(targetDir)\n\t\tif rmErr != nil {\n\t\t\tfmt.Printf(\"\\u2717 Failed to remove partial install: %v\\n\", rmErr)\n\t\t}\n\t\treturn fmt.Errorf(\"✗ failed to install skill: %w\", err)\n\t}\n\n\tif result.IsMalwareBlocked {\n\t\trmErr := os.RemoveAll(targetDir)\n\t\tif rmErr != nil {\n\t\t\tfmt.Printf(\"\\u2717 Failed to remove partial install: %v\\n\", rmErr)\n\t\t}\n\n\t\treturn fmt.Errorf(\"\\u2717 Skill '%s' is flagged as malicious and cannot be installed.\\n\", target)\n\t}\n\n\tif result.IsSuspicious {\n\t\tfmt.Printf(\"\\u26a0\\ufe0f  Warning: skill '%s' is flagged as suspicious.\\n\", target)\n\t}\n\n\tif !workspaceHasValidSkillDirectory(workspace, dirName) {\n\t\t_ = os.RemoveAll(targetDir)\n\t\treturn fmt.Errorf(\"✗ failed to install skill: registry archive for %q is not a valid skill\", target)","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/skills/helpers.go#L73-L109","documentation":"registry.DownloadAndInstall failed — the download, archive fetch, or extraction errored within the hard-coded 60-second context. The partial target directory is automatically removed and the underlying error is wrapped, so the wrapped text distinguishes network, HTTP, and extraction failures.","triggerScenarios":"A network failure or the 60s context deadline expiring while downloading the archive, the registry returning an error for the target, or a corrupt/unreadable archive — helpers.go:91 (the helper RemoveAlls the partial install on this path).","commonSituations":"Slow link exceeding the fixed 60s deadline, registry rate-limiting or downtime, target not published at the requested version, or an upstream git host outage for git-backed registries.","solutions":["Read the wrapped cause — it separates timeout vs HTTP error vs extraction failure.","Retry once or twice with backoff; transient registry/network errors dominate.","Verify the target exists on the registry's site.","For large skills on slow links, note the 60s timeout is fixed in code — retry on a faster network or raise the constant locally."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"result, err := registry.DownloadAndInstall(ctx, target, \"\", targetDir)\nif err != nil {\n    // helper already removed the partial dir; safe to retry idempotently\n    if ctx.Err() == context.DeadlineExceeded || isRetryable(err) {\n        return retryWithBackoff()\n    }\n    return fmt.Errorf(\"failed to install skill: %w\", err)\n}","preventionTips":["Retry installs once after backoff before reporting failure.","Account for the 60s download deadline when timing automation.","Pin known-good versions to avoid not-found paths."],"tags":["network","install","registry","timeout","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}