{"record":{"id":"b26229c3f96e7f76","repo":"flipped-aurora/gin-vue-admin","slug":"w-b26229","errorCode":null,"errorMessage":"下载技能失败: %w","messagePattern":"下载技能失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/sys_skills.go","lineNumber":410,"sourceCode":"\t}\n\n\tbody, err := json.Marshal(map[string]interface{}{\n\t\t\"plugin_id\": req.ID,\n\t\t\"version\":   req.Version,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"构建下载请求失败: %w\", err)\n\t}\n\n\tdownloadReq, err := http.NewRequest(http.MethodPost, \"https://plugin.gin-vue-admin.com/api/shopPlugin/downloadSkill\", bytes.NewReader(body))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"构建下载请求失败: %w\", err)\n\t}\n\tdownloadReq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tdownloadResp, err := http.DefaultClient.Do(downloadReq)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"下载技能失败: %w\", err)\n\t}\n\tdefer downloadResp.Body.Close()\n\n\tif downloadResp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"下载技能失败, HTTP状态码: %d\", downloadResp.StatusCode)\n\t}\n\n\tmetaBody, err := io.ReadAll(downloadResp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"读取下载结果失败: %w\", err)\n\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 {","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/sys_skills.go#L392-L428","documentation":"The HTTP transport itself failed when POSTing to the skill marketplace (http.DefaultClient.Do returned an error). This means no HTTP response was received: DNS resolution failure, connection refused/timeout, TLS handshake failure, or a request-context cancellation.","triggerScenarios":"Calling the skill download API when the server has no internet access, DNS for plugin.gin-vue-admin.com fails, the marketplace is down, a firewall/proxy blocks outbound HTTPS (443), or TLS interception breaks the handshake.","commonSituations":"Air-gapped or intranet-only deployments; expired system CA roots breaking TLS; corporate proxies requiring configuration; marketplace outage; no proxy env vars in containers.","solutions":["Check outbound network/DNS from the server: curl -v https://plugin.gin-vue-admin.com/api/shopPlugin/downloadSkill","Confirm HTTPS (443) egress is allowed by firewall/security groups; configure HTTP_PROXY/HTTPS_PROXY if behind a proxy","If using a custom CA or TLS-intercepting proxy, add it to the system trust store","Retry later if the marketplace is down; surface the wrapped error via errors.Unwrap to see the net.OpError cause"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"conn, err := net.DialTimeout(\"tcp\", \"plugin.gin-vue-admin.com:443\", 5*time.Second)\nif err != nil { /* no egress to marketplace; short-circuit */ }\nelse { conn.Close() }","typeGuard":"func isNetworkErr(err error) bool {\n    var ne net.Error\n    return errors.As(err, &ne) || errors.Is(err, syscall.ECONNREFUSED) || errors.Is(err, context.DeadlineExceeded)\n}","tryCatchPattern":"if err := DownloadOnlineSkill(ctx, req); err != nil {\n    var ne net.Error\n    if errors.As(errors.Unwrap(errors.Unwrap(err)), &ne) {\n        // transient network failure: retry with backoff or surface 'network unavailable'\n    }\n}","preventionTips":["Ensure outbound HTTPS (443) egress from the server; document proxy vars","Set a timeout on the HTTP client instead of http.DefaultClient","Add retry with exponential backoff for marketplace calls","Monitor DNS resolution in the deployment environment"],"tags":["go","network","http-client","dns"],"backgroundTag":"http-request-failed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}