{"record":{"id":"339a7ba8c924913e","repo":"henrygd/beszel","slug":"http-get-w","errorCode":null,"errorMessage":"http get: %w","messagePattern":"http get: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/tools/fetchsmartctl/main.go","lineNumber":60,"sourceCode":"}\n\nfunc downloadFile(url, dest, shaHex string) error {\n\t// Prepare destination\n\tif err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {\n\t\treturn fmt.Errorf(\"create dir: %w\", err)\n\t}\n\n\t// HTTP client\n\tclient := &http.Client{Timeout: 60 * time.Second}\n\treq, err := http.NewRequest(http.MethodGet, url, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new request: %w\", err)\n\t}\n\treq.Header.Set(\"User-Agent\", \"beszel-fetchsmartctl/1.0\")\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"http get: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\treturn fmt.Errorf(\"unexpected HTTP status: %s\", resp.Status)\n\t}\n\n\ttmp := dest + \".tmp\"\n\tf, err := os.OpenFile(tmp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open tmp: %w\", err)\n\t}\n\n\t// Determine hash algorithm based on length (SHA1=40, SHA256=64)\n\tvar hasher hash.Hash\n\tif shaHex := strings.TrimSpace(shaHex); shaHex != \"\" {\n\t\tcleanSha := strings.ToLower(strings.ReplaceAll(shaHex, \" \", \"\"))\n\t\tswitch len(cleanSha) {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/tools/fetchsmartctl/main.go#L42-L78","documentation":"Thrown by downloadFile when the HTTP client's Do call fails — the request never completed. This wraps DNS resolution failures, connection refused/timeouts (client timeout is 60s), TLS errors, or proxy problems. No response body is available at this point.","triggerScenarios":"Running fetchsmartctl behind a firewall/proxy, offline, or against an unreachable/moved download host; TLS handshake failure.","commonSituations":"CI runners without internet egress; corporate proxy requiring auth; smartmontools download mirror temporarily down or DNS records changed; IPv6-only egress failures.","solutions":["Test connectivity to the URL with curl from the same machine","Check DNS resolution and proxy env vars (HTTP_PROXY/HTTPS_PROXY)","Retry later if the mirror is transiently down; pin to an alternate mirror URL","Inspect the wrapped %w error: dial tcp / no such host / x509 tell you the layer"],"exampleFix":"// before\nresp, err := client.Do(req)\nif err != nil {\n\treturn fmt.Errorf(\"http get: %w\", err)\n}\n// after\nvar resp *http.Response\nfor i := 0; i < 3; i++ {\n\tresp, err = client.Do(req)\n\tif err == nil {\n\t\tbreak\n\t}\n\ttime.Sleep(time.Duration(1<<i) * time.Second) // retry transient network errors\n}\nif err != nil {\n\treturn fmt.Errorf(\"http get: %w\", err)\n}","handlingStrategy":"retry","validationCode":"conn, err := net.DialTimeout(\"tcp\", host+\":443\", 5*time.Second)\nif err != nil {\n\treturn fmt.Errorf(\"cannot reach %s: %w\", host, err)\n}\nconn.Close()","typeGuard":null,"tryCatchPattern":"if err := downloadFile(url, dest, sha); err != nil {\n\tif strings.HasPrefix(err.Error(), \"http get:\") {\n\t\ttime.Sleep(5 * time.Second)\n\t\terr = downloadFile(url, dest, sha) // retry transient network failure\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n}","preventionTips":["Configure HTTP_PROXY/HTTPS_PROXY correctly in CI and corporate networks","Add retry with backoff around downloads","Pin to a reliable mirror and have a fallback URL","Verify DNS/egress before running download steps in CI"],"tags":["network","http","download"],"backgroundTag":"connection-refused","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}