{"record":{"id":"563e58d2b327c4bb","repo":"GopeedLab/gopeed","slug":"connection-d-failed-retries-d-err-v","errorCode":null,"errorMessage":"connection %d failed: retries=%d, err=%v","messagePattern":"connection (.+?) failed: retries=(.+?), err=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/protocol/http/fetcher.go","lineNumber":1539,"sourceCode":"\t\t}\n\t}\n\n\t// If total downloaded matches file size, consider it a success regardless of connection failures\n\tdownloadComplete := f.meta.Res.Size > 0 && totalDownloaded >= f.meta.Res.Size\n\n\t// Check for any errors, but ignore 403 (server connection limit) errors if download completed\n\tvar finalErr error\n\tif !downloadComplete && !allChunksComplete {\n\t\tfor _, conn := range f.connections {\n\t\t\tif conn.State == connFailed && conn.failed {\n\t\t\t\t// Skip 403 errors (server connection limit) - these are expected when exceeding server's limit\n\t\t\t\tif re := extractRequestError(conn.lastErr); re != nil && re.Code == 403 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif re := extractRequestError(conn.lastErr); re != nil {\n\t\t\t\t\tfinalErr = fmt.Errorf(\"connection %d failed: retries=%d, status=%d\", conn.ID, conn.retryTimes, re.Code)\n\t\t\t\t} else if conn.lastErr != nil {\n\t\t\t\t\tfinalErr = fmt.Errorf(\"connection %d failed: retries=%d, err=%v\", conn.ID, conn.retryTimes, conn.lastErr)\n\t\t\t\t} else {\n\t\t\t\t\tfinalErr = fmt.Errorf(\"connection %d failed: retries=%d\", conn.ID, conn.retryTimes)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tf.connMu.Unlock()\n\n\t// Close the file before signaling completion\n\t// This ensures the file handle is released before Wait() returns\n\tf.fileMu.Lock()\n\tif f.file != nil {\n\t\tf.file.Close()\n\t\tf.file = nil\n\t}\n\tf.fileMu.Unlock()\n","sourceCodeStart":1521,"sourceCodeEnd":1557,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/internal/protocol/http/fetcher.go#L1521-L1557","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nerr := fetcher.Wait() // \"connection 0 failed: retries=6, err=read tcp ...: connection reset by peer\"\n\n// after\nif err := fetcher.Wait(); err != nil {\n    opts.Connections = 1 // survive throttling middleboxes\n    return retryWithBackoff(func() error {\n        f := http.NewFetcher(...)\n        if err := f.Resolve(req, opts); err != nil { return err }\n        if err := f.Start(); err != nil { return err }\n        return f.Wait()\n    }, 3)\n}","handlingStrategy":"retry","validationCode":"// Cheap connectivity probe before (re)starting a long download\nfunc linkHealthy(host string) error {\n    conn, err := net.DialTimeout(\"tcp\", host, 3*time.Second)\n    if err != nil {\n        return err\n    }\n    return conn.Close()\n}","typeGuard":null,"tryCatchPattern":"if err := fetcher.Wait(); err != nil {\n    if strings.Contains(err.Error(), \"connection \") && strings.Contains(err.Error(), \"err=\") {\n        return retryWithBackoff(func() error { // fresh Fetcher, same target\n            return runDownload(req, opts)\n        }, 3)\n    }\n    return err\n}","preventionTips":["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"],"tags":["network","download","connection","transport","retries"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}