{"record":{"id":"907688c1d6e01536","repo":"henrygd/beszel","slug":"unexpected-http-status-s","errorCode":null,"errorMessage":"unexpected HTTP status: %s","messagePattern":"unexpected HTTP status: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/tools/fetchsmartctl/main.go","lineNumber":65,"sourceCode":"\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) {\n\t\tcase 40:\n\t\t\thasher = sha1.New()\n\t\tcase 64:\n\t\t\thasher = sha256.New()\n\t\tdefault:","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/tools/fetchsmartctl/main.go#L47-L83","documentation":"Thrown by downloadFile when the server responds with a non-2xx status code (e.g. 404 Not Found, 403 Forbidden, 503). The message includes the full status string so you can see exactly what the remote returned. The download is aborted before writing any bytes.","triggerScenarios":"The smartmontools release URL is wrong, the version/tag no longer exists, the CDN returns 403/404, or a proxy intercepts with an error page.","commonSituations":"Upstream release replaced/moved (version bump in the tool's URL list); rate limiting from GitHub; typo'd version in build config; corporate proxy blocking the domain.","solutions":["Read the status in the message: 404 means the URL/version is wrong — update to the current release URL","403 often means rate limiting or proxy auth — check proxy settings or retry later","5xx means server-side trouble — retry with backoff","Verify the URL manually with curl -I"],"exampleFix":"// before\nif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\treturn fmt.Errorf(\"unexpected HTTP status: %s\", resp.Status)\n}\n// after\nif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\treturn fmt.Errorf(\"unexpected HTTP status %s for %s\", resp.Status, url)\n}","handlingStrategy":"retry","validationCode":"resp, err := http.Head(url)\nif err == nil && (resp.StatusCode < 200 || resp.StatusCode >= 300) {\n\treturn fmt.Errorf(\"URL %s returns %s before download\", url, resp.Status)\n}","typeGuard":null,"tryCatchPattern":"if err := downloadFile(url, dest, sha); err != nil {\n\tif strings.Contains(err.Error(), \"unexpected HTTP status\") {\n\t\tfmt.Printf(\"server said: %s — check the release URL/version\\n\", err)\n\t\tos.Exit(3)\n\t}\n\treturn err\n}","preventionTips":["Verify release URLs with curl -I after every version bump","Handle GitHub rate limits (use tokens or mirrors) in CI","Re-check the smartmontools release tag when 404s appear","Watch for proxy-injected 403/503 pages in corporate environments"],"tags":["http","download","build-tool"],"backgroundTag":"http-404-not-found","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}