{"record":{"id":"1a5c686b64e64853","repo":"flipped-aurora/gin-vue-admin","slug":"w-1a5c68","errorCode":null,"errorMessage":"下载压缩包失败: %w","messagePattern":"下载压缩包失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/sys_skills.go","lineNumber":439,"sourceCode":"\t}\n\n\tvar meta struct {\n\t\tData struct {\n\t\t\tURL string `json:\"url\"`\n\t\t} `json:\"data\"`\n\t}\n\tif err = json.Unmarshal(metaBody, &meta); err != nil {\n\t\treturn fmt.Errorf(\"解析下载结果失败: %w\", err)\n\t}\n\n\trealDownloadURL := strings.TrimSpace(meta.Data.URL)\n\tif realDownloadURL == \"\" {\n\t\treturn errors.New(\"下载结果缺少 url\")\n\t}\n\n\tzipResp, err := http.Get(realDownloadURL)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"下载压缩包失败: %w\", err)\n\t}\n\tdefer zipResp.Body.Close()\n\n\tif zipResp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"下载压缩包失败, HTTP状态码: %d\", zipResp.StatusCode)\n\t}\n\n\ttmpFile, err := os.CreateTemp(\"\", \"gva-skill-*.zip\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"创建临时文件失败: %w\", err)\n\t}\n\ttmpPath := tmpFile.Name()\n\tdefer os.Remove(tmpPath)\n\n\tif _, err = io.Copy(tmpFile, zipResp.Body); err != nil {\n\t\ttmpFile.Close()\n\t\treturn fmt.Errorf(\"保存技能包失败: %w\", err)\n\t}","sourceCodeStart":421,"sourceCodeEnd":457,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/sys_skills.go#L421-L457","documentation":"The second hop — http.Get on the real zip download URL returned by the marketplace — failed at the transport level. Note this URL is server-controlled output; failures include DNS errors, TLS errors, connection refused/timeouts, and also http.Get's built-in rejection of non-http(s) schemes (it returns 'unsupported protocol scheme').","triggerScenarios":"Calling DownloadOnlineSkill when the returned real download URL points to a host that's unreachable, uses a non-http scheme, or the server lacks egress to it (e.g. internal/OSS URL not reachable from the server's network).","commonSituations":"Marketplace returns an object-storage URL blocked by the server's firewall; no DNS for the CDN host; URL scheme mismatch; China-network reachability issues with the CDN.","solutions":["Log the wrapped net error and the realDownloadURL to see which host/scheme failed","Confirm the server can reach the returned host (curl -v <url>)","If the marketplace returns an unsupported scheme, validate it before calling http.Get","Add proxy configuration or allowlist the CDN/object-storage domain in egress rules"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"u, err := url.Parse(realDownloadURL)\nif err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n    // reject bad scheme before http.Get (avoids 'unsupported protocol scheme')\n}\nif host := u.Host; host != \"\" {\n    if c, e := net.DialTimeout(\"tcp\", host+\":443\", 5*time.Second); e != nil { /* unreachable */ } else { c.Close() }\n}","typeGuard":"func isDownloadableURL(raw string) bool {\n    u, err := url.Parse(raw)\n    return err == nil && (u.Scheme == \"https\" || u.Scheme == \"http\") && u.Host != \"\"\n}","tryCatchPattern":"if err := DownloadOnlineSkill(ctx, req); err != nil && strings.Contains(err.Error(), \"下载压缩包失败:\") {\n    // transport failure on the zip URL; check egress/DNS for the returned host and retry\n}","preventionTips":["Validate the returned URL's scheme/host before fetching","Allowlist the marketplace's CDN/storage domains in egress rules","Set client timeouts instead of bare http.Get","Retry transient transport errors with backoff"],"tags":["go","network","http-client","download"],"backgroundTag":"http-request-failed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}