GopeedLab/gopeed · error
connection %d failed: retries=%d, err=%v
Error message
connection %d failed: retries=%d, err=%v
What it means
Same completion-time aggregation as the status variant, but chosen when conn.lastErr is a transport-level error rather than an HTTP status (extractRequestError returned nil). The underlying error is embedded with %v, so the message shows the real network cause: reset, timeout, TLS failure, DNS, and so on.
Source
Thrown at internal/protocol/http/fetcher.go:1539
}
}
// 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
}
f.fileMu.Unlock()
View on GitHub (pinned to 7b7327ffb3)
Solutions
- Read the err=%v portion first: it names the actual transport failure and points at the fix
- For reset/timeout: retry with backoff and fewer parallel connections
- For TLS errors: check certificate chain and system CA store; for DNS: check resolver
- For proxy environments: configure the proxy explicitly and test a single-connection download first
Example fix
// before
err := fetcher.Wait() // "connection 0 failed: retries=6, err=read tcp ...: connection reset by peer"
// after
if err := fetcher.Wait(); err != nil {
opts.Connections = 1 // survive throttling middleboxes
return retryWithBackoff(func() error {
f := http.NewFetcher(...)
if err := f.Resolve(req, opts); err != nil { return err }
if err := f.Start(); err != nil { return err }
return f.Wait()
}, 3)
} Defensive patterns
Strategy: retry
Validate before calling
// Cheap connectivity probe before (re)starting a long download
func linkHealthy(host string) error {
conn, err := net.DialTimeout("tcp", host, 3*time.Second)
if err != nil {
return err
}
return conn.Close()
} Try / catch
if err := fetcher.Wait(); err != nil {
if strings.Contains(err.Error(), "connection ") && strings.Contains(err.Error(), "err=") {
return retryWithBackoff(func() error { // fresh Fetcher, same target
return runDownload(req, opts)
}, 3)
}
return err
} Prevention
- Use exponential backoff with jitter for transport failures
- Pin the proxy configuration explicitly rather than relying on ambient env vars
- Lower parallelism on unreliable links — fewer connections fail together
When it happens
Trigger: Connection reset by peer or RST from a firewall mid-transfer, TLS handshake failure against a misconfigured host, DNS resolution drop, proxy refusing CONNECT, or a local socket timeout after retries were exhausted.
Common situations: Flaky Wi-Fi/VPN links, corporate proxies that cut long-lived range connections, IPv6 breakage, captive portals intercepting the transfer, or MTU/blackhole issues that stall a chunk until it times out.
Related errors
- connection %d failed: retries=%d, status=%d
- connection %d failed: retries=%d
- redirect failed
- too many redirects
- invalid resource file name
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/563e58d2b327c4bb.
Report an issue: GitHub.