Billionmail/BillionMail · error

download file failed:

Error message

download file failed: 

What it means

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.

Source

Thrown at core/internal/service/public/common.go:2050

	// Set request headers
	if headers != nil {
		for k, v := range headers {
			req.Header.Set(k, v)
		}
	}

	// Send request
	var resp *http.Response

	if resp, err = client.Do(req.WithContext(ctx)); err != nil {
		return
	}

	defer resp.Body.Close()

	// Download failed
	if resp.StatusCode < http.StatusOK && resp.StatusCode >= http.StatusMultipleChoices {
		return errors.New("download file failed: " + resp.Status)
	}

	// Create directory
	dir := filepath.Dir(dst)
	if !FileExists(dir) {
		if err = os.MkdirAll(dir, 0755); err != nil {
			return
		}
	}

	// Create temporary file
	tmpFile := dst + ".tmp"

	// Open file
	var fp *os.File
	if fp, err = os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644); err != nil {
		return
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check the URL is correct and the asset still exists (curl -I the URL to see the status).
  2. For 403/429, add required auth headers or wait for the rate limit; consider passing an authenticated client to the download call.
  3. Verify network/proxy settings; use the returned resp.Status string to identify the exact HTTP code before retrying.
  4. For 301/302, ensure the final redirect target is reachable and does not loop.

Example fix

// before
err := public.DownloadFile("https://example.com/old-file.tar.gz", dst) // 404
// after
url := "https://example.com/new-file-v2.tar.gz"
if err := public.DownloadFile(url, dst); err != nil {
    log.Printf("download %s failed: %v", url, err)
    return fmt.Errorf("check asset exists at %s: %w", url, err)
}
Defensive patterns

Strategy: retry

Validate before calling

resp, err := http.Head(url)
if err != nil || resp.StatusCode != http.StatusOK {
    return fmt.Errorf("asset not downloadable, status: %d", resp.StatusCode)
}

Try / catch

var lastErr error
for attempt := 0; attempt < 3; attempt++ {
    err := public.DownloadFile(url, dst)
    if err == nil {
        return nil
    }
    lastErr = err
    if strings.Contains(err.Error(), "4") && !strings.Contains(err.Error(), "429") {
        break // client errors won't fix themselves
    }
    time.Sleep(time.Duration(attempt+1) * 2 * time.Second)
}
return lastErr

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/d45586d1d4169639. Report an issue: GitHub.