{"record":{"id":"93bb4839225569f6","repo":"flipped-aurora/gin-vue-admin","slug":"http-d-93bb48","errorCode":null,"errorMessage":"下载压缩包失败, HTTP状态码: %d","messagePattern":"下载压缩包失败, HTTP状态码: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/sys_skills.go","lineNumber":444,"sourceCode":"\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}\n\ttmpFile.Close()\n\n\tif err = extractZipToDir(tmpPath, skillsDir); err != nil {\n\t\treturn fmt.Errorf(\"解压技能包失败: %w\", err)\n\t}","sourceCodeStart":426,"sourceCodeEnd":462,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/sys_skills.go#L426-L462","documentation":"The zip file download from the real URL returned a non-200 HTTP status. The code requires 200 before saving the archive to a temp file; any other status (403 expired link, 404 missing file, 5xx storage error) produces this error.","triggerScenarios":"Calling DownloadOnlineSkill when the marketplace-issued download URL has expired (signed URL expiry), the object was deleted, the storage backend rejects the request, or a geo/ACL restriction returns 403.","commonSituations":"Signed object-storage URLs with short TTL consumed too late; CDN hotlink protection; storage bucket permission changes; marketplace repackaging skills so old URLs 404.","solutions":["Retry the whole DownloadOnlineSkill flow so a fresh download URL is issued (don't reuse old URLs)","Check the status code and response body of the zip request for auth/expiry hints","Verify the marketplace skill still exists and is downloadable","If 403 persists, check whether the storage host requires referer/token headers that http.Get doesn't send"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isZipDownloadStatusErr(err error) (int, bool) {\n    m := regexp.MustCompile(`下载压缩包失败, HTTP状态码: (\\d+)`).FindStringSubmatch(err.Error())\n    if len(m) < 2 { return 0, false }\n    code, _ := strconv.Atoi(m[1])\n    return code, true\n}","tryCatchPattern":"if err := DownloadOnlineSkill(ctx, req); err != nil {\n    if code, ok := isZipDownloadStatusErr(err); ok && (code == 403 || code == 404) {\n        // download URL expired/invalid: restart the whole flow to get a fresh URL\n    }\n}","preventionTips":["Never cache or reuse marketplace-issued download URLs — request a fresh one per download","Download promptly after obtaining the URL to beat signed-URL expiry","Check status + body of the zip response for expiry/auth hints","Monitor object-storage permission changes"],"tags":["go","http","http-status","download"],"backgroundTag":"unexpected-http-status","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}