{"record":{"id":"6eb059dcbe8496ba","repo":"GopeedLab/gopeed","slug":"connection-d-failed-retries-d","errorCode":null,"errorMessage":"connection %d failed: retries=%d","messagePattern":"connection (.+?) failed: retries=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/protocol/http/fetcher.go","lineNumber":1541,"sourceCode":"\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\n\tif finalErr != nil {\n\t\tf.setState(stateError)","sourceCodeStart":1523,"sourceCodeEnd":1559,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/internal/protocol/http/fetcher.go#L1523-L1559","documentation":"The fallback branch of the same aggregation: a connection is flagged connFailed and failed, but lastErr is nil, so no cause can be reported. The download still failed; the reason was simply never recorded by whichever code path marked the connection failed.","triggerScenarios":"A code path (or race) that sets State = connFailed and failed = true without assigning lastErr — for example a panic-recovered worker or a stop-path that reuses the failure flag. From the caller's view it looks like a failure with no network or status cause.","commonSituations":"Rare in practice; when it appears it usually accompanies context cancellation racing the retry logic, or a bug in custom builds. Because the cause is missing, diagnosis needs connection-level logging enabled beforehand.","solutions":["Reproduce with verbose/debug logging for the fetcher and its connections to recover the missing cause","Treat it operationally like the transport-error variant: fresh Fetcher, retry with backoff","If reproducible, inspect the code paths that mark connections failed and ensure they store lastErr (this message existing means one does not)","Check whether the task was cancelled or paused concurrently — cancellation races can leave connections in a bare failed state"],"exampleFix":"// before\nif err := fetcher.Wait(); err != nil { return err } // \"connection 1 failed: retries=6\" — no cause\n\n// after\nif err := fetcher.Wait(); err != nil {\n    log.Printf(\"download failed (cause not captured): %v\", err)\n    return retryFreshFetcher(req, opts, 2) // rebuild fetcher; keep logs for diagnosis\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := fetcher.Wait(); err != nil {\n    msg := err.Error()\n    if strings.Contains(msg, \"failed: retries=\") && !strings.Contains(msg, \"err=\") && !strings.Contains(msg, \"status=\") {\n        log.Printf(\"download failed without a recorded cause: %v\", msg)\n    }\n    return retryFresh(req, opts, 2)\n}","preventionTips":["Enable verbose connection logging before you need it — this variant carries no cause","Avoid cancelling/pausing downloads concurrently with retries; races can strip the error","If reproducible, report it: a failed connection should always record lastErr"],"tags":["download","connection","diagnostics","missing-cause"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}