henrygd/beszel · error

http get: %w

Error message

http get: %w

What it means

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.

Source

Thrown at agent/tools/fetchsmartctl/main.go:60

}

func downloadFile(url, dest, shaHex string) error {
	// Prepare destination
	if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
		return fmt.Errorf("create dir: %w", err)
	}

	// HTTP client
	client := &http.Client{Timeout: 60 * time.Second}
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return fmt.Errorf("new request: %w", err)
	}
	req.Header.Set("User-Agent", "beszel-fetchsmartctl/1.0")

	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("http get: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return fmt.Errorf("unexpected HTTP status: %s", resp.Status)
	}

	tmp := dest + ".tmp"
	f, err := os.OpenFile(tmp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
	if err != nil {
		return fmt.Errorf("open tmp: %w", err)
	}

	// Determine hash algorithm based on length (SHA1=40, SHA256=64)
	var hasher hash.Hash
	if shaHex := strings.TrimSpace(shaHex); shaHex != "" {
		cleanSha := strings.ToLower(strings.ReplaceAll(shaHex, " ", ""))
		switch len(cleanSha) {

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Test connectivity to the URL with curl from the same machine
  2. Check DNS resolution and proxy env vars (HTTP_PROXY/HTTPS_PROXY)
  3. Retry later if the mirror is transiently down; pin to an alternate mirror URL
  4. Inspect the wrapped %w error: dial tcp / no such host / x509 tell you the layer

Example fix

// before
resp, err := client.Do(req)
if err != nil {
	return fmt.Errorf("http get: %w", err)
}
// after
var resp *http.Response
for i := 0; i < 3; i++ {
	resp, err = client.Do(req)
	if err == nil {
		break
	}
	time.Sleep(time.Duration(1<<i) * time.Second) // retry transient network errors
}
if err != nil {
	return fmt.Errorf("http get: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", host+":443", 5*time.Second)
if err != nil {
	return fmt.Errorf("cannot reach %s: %w", host, err)
}
conn.Close()

Try / catch

if err := downloadFile(url, dest, sha); err != nil {
	if strings.HasPrefix(err.Error(), "http get:") {
		time.Sleep(5 * time.Second)
		err = downloadFile(url, dest, sha) // retry transient network failure
	}
	if err != nil {
		return err
	}
}

Prevention

When it happens

Trigger: Running fetchsmartctl behind a firewall/proxy, offline, or against an unreachable/moved download host; TLS handshake failure.

Common situations: CI runners without internet egress; corporate proxy requiring auth; smartmontools download mirror temporarily down or DNS records changed; IPv6-only egress failures.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/339a7ba8c924913e. Report an issue: GitHub.