GopeedLab/gopeed · error
connection %d failed: retries=%d, status=%d
Error message
connection %d failed: retries=%d, status=%d
What it means
Produced in onDownloadComplete (internal/protocol/http/fetcher.go:1537) when the download did not finish and at least one connection ended in connFailed after exhausting its retries. This variant is chosen when the connection's last error is a RequestError carrying an HTTP status code; that code is included in the message. 403 is deliberately skipped because it usually signals a server-side per-IP connection limit, not a real failure.
Source
Thrown at internal/protocol/http/fetcher.go:1537
allChunksComplete = false
break
}
}
// If total downloaded matches file size, consider it a success regardless of connection failures
downloadComplete := f.meta.Res.Size > 0 && totalDownloaded >= f.meta.Res.Size
// Check for any errors, but ignore 403 (server connection limit) errors if download completed
var finalErr error
if !downloadComplete && !allChunksComplete {
for _, conn := range f.connections {
if conn.State == connFailed && conn.failed {
// Skip 403 errors (server connection limit) - these are expected when exceeding server's limit
if re := extractRequestError(conn.lastErr); re != nil && re.Code == 403 {
continue
}
if re := extractRequestError(conn.lastErr); re != nil {
finalErr = fmt.Errorf("connection %d failed: retries=%d, status=%d", conn.ID, conn.retryTimes, re.Code)
} else if conn.lastErr != nil {
finalErr = fmt.Errorf("connection %d failed: retries=%d, err=%v", conn.ID, conn.retryTimes, conn.lastErr)
} else {
finalErr = fmt.Errorf("connection %d failed: retries=%d", conn.ID, conn.retryTimes)
}
break
}
}
}
f.connMu.Unlock()
// Close the file before signaling completion
// This ensures the file handle is released before Wait() returns
f.fileMu.Lock()
if f.file != nil {
f.file.Close()
f.file = nil
}View on GitHub (pinned to 7b7327ffb3)
Solutions
- Match the status: 404/410 means the URL is gone — re-resolve the resource; 401/403 with signed URLs means re-sign; 5xx means retry later
- Verify with a HEAD/GET that the URL still returns 200 and Accept-Ranges: bytes before retrying a multi-connection download
- Reduce the connection count (some servers throttle parallel ranges)
- Retry the whole task from a fresh Fetcher instead of re-Start
Example fix
// before
err := fetcher.Wait() // "connection 2 failed: retries=5, status=416"
// after
// preflight before a multi-connection retry
resp, err := http.Head(url)
if err == nil && resp.StatusCode == 200 && resp.Header.Get("Accept-Ranges") == "bytes" {
// safe to retry with range connections
} else {
// fall back to a single connection or re-resolve the URL
} Defensive patterns
Strategy: retry
Validate before calling
// Preflight the URL before a multi-connection download
func supportsRanges(url string) bool {
resp, err := http.Head(url)
if err != nil || resp.StatusCode != http.StatusOK {
return false
}
return resp.Header.Get("Accept-Ranges") == "bytes"
} Try / catch
err := fetcher.Wait()
if err != nil && strings.Contains(err.Error(), "failed: retries=") && strings.Contains(err.Error(), "status=") {
// extract the status and decide: re-resolve for 401/404, backoff-retry for 5xx
return retryFresh(req, opts, 3) // new Fetcher per attempt
} Prevention
- Check Accept-Ranges and a 200 status before enabling chunked/multi-connection mode
- For signed URLs, ensure TTL comfortably exceeds the expected download time
- Keep the connection count modest on hosts that throttle parallel ranges
When it happens
Trigger: A chunk connection repeatedly received 404 (file removed mid-download), 416 (range no longer satisfiable after the file changed), 500/502/503 (server errors), or 401 (signed URL expired) until the retry budget ran out.
Common situations: Expired pre-signed URLs on CDN hosts, servers that reject many parallel range requests, files deleted or replaced while a long download was paused, or a proxy stripping Range support so chunked workers fail.
Related errors
- connection %d failed: retries=%d, err=%v
- connection %d failed: retries=%d
- redirect failed
- too many redirects
- invalid range
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/545ea0aef6f24d69.
Report an issue: GitHub.