{"record":{"id":"d45586d1d4169639","repo":"Billionmail/BillionMail","slug":"download-file-failed","errorCode":null,"errorMessage":"download file failed: ","messagePattern":"download file failed: ","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/public/common.go","lineNumber":2050,"sourceCode":"\t// Set request headers\n\tif headers != nil {\n\t\tfor k, v := range headers {\n\t\t\treq.Header.Set(k, v)\n\t\t}\n\t}\n\n\t// Send request\n\tvar resp *http.Response\n\n\tif resp, err = client.Do(req.WithContext(ctx)); err != nil {\n\t\treturn\n\t}\n\n\tdefer resp.Body.Close()\n\n\t// Download failed\n\tif resp.StatusCode < http.StatusOK && resp.StatusCode >= http.StatusMultipleChoices {\n\t\treturn errors.New(\"download file failed: \" + resp.Status)\n\t}\n\n\t// Create directory\n\tdir := filepath.Dir(dst)\n\tif !FileExists(dir) {\n\t\tif err = os.MkdirAll(dir, 0755); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Create temporary file\n\ttmpFile := dst + \".tmp\"\n\n\t// Open file\n\tvar fp *os.File\n\tif fp, err = os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644); err != nil {\n\t\treturn\n\t}","sourceCodeStart":2032,"sourceCodeEnd":2068,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/public/common.go#L2032-L2068","documentation":"The DownloadFile helper in core/internal/service/public/common.go performs an HTTP GET and checks the response status. When the status code is not in the 2xx range (below 200 or >= 300), it returns \"download file failed: <resp.Status>\" instead of writing the body to dst. The remote response body is discarded, so the original server error text is only available via resp.Status.","triggerScenarios":"Calling DownloadFile(url, dst) where the server replies 404 Not Found, 403 Forbidden, 500, 301/302 terminated as non-2xx, or any non-2xx status; also firewalled/proxied endpoints returning HTML error pages with 4xx/5xx codes.","commonSituations":"Downloading an installer/asset from a URL that was renamed or deleted, GitHub release asset URLs that moved, rate-limited endpoints returning 429, or self-signed/intercepting proxies returning 403.","solutions":["Check the URL is correct and the asset still exists (curl -I the URL to see the status).","For 403/429, add required auth headers or wait for the rate limit; consider passing an authenticated client to the download call.","Verify network/proxy settings; use the returned resp.Status string to identify the exact HTTP code before retrying.","For 301/302, ensure the final redirect target is reachable and does not loop."],"exampleFix":"// before\nerr := public.DownloadFile(\"https://example.com/old-file.tar.gz\", dst) // 404\n// after\nurl := \"https://example.com/new-file-v2.tar.gz\"\nif err := public.DownloadFile(url, dst); err != nil {\n    log.Printf(\"download %s failed: %v\", url, err)\n    return fmt.Errorf(\"check asset exists at %s: %w\", url, err)\n}","handlingStrategy":"retry","validationCode":"resp, err := http.Head(url)\nif err != nil || resp.StatusCode != http.StatusOK {\n    return fmt.Errorf(\"asset not downloadable, status: %d\", resp.StatusCode)\n}","typeGuard":null,"tryCatchPattern":"var lastErr error\nfor attempt := 0; attempt < 3; attempt++ {\n    err := public.DownloadFile(url, dst)\n    if err == nil {\n        return nil\n    }\n    lastErr = err\n    if strings.Contains(err.Error(), \"4\") && !strings.Contains(err.Error(), \"429\") {\n        break // client errors won't fix themselves\n    }\n    time.Sleep(time.Duration(attempt+1) * 2 * time.Second)\n}\nreturn lastErr","preventionTips":["HEAD-check the URL before downloading","Pin asset URLs to immutable versions rather than moving 'latest' links","Log resp.Status details included in the error for diagnosis","Handle 429 with exponential backoff"],"tags":["network","http","download","go"],"backgroundTag":"http-non-2xx-response","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}