{"record":{"id":"5389c4c7fc0263e3","repo":"AlistGo/alist","slug":"http-request-failure-status-d","errorCode":null,"errorMessage":"http request failure,status: %d","messagePattern":"http request failure,status: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/net/request.go","lineNumber":414,"sourceCode":"\tresp, err := d.cfg.HttpClient(d.ctx, params)\n\tif err != nil {\n\t\tif resp == nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif resp.StatusCode == http.StatusRequestedRangeNotSatisfiable {\n\t\t\treturn 0, err\n\t\t}\n\t\tif ch.id == 0 { //第1个任务 有限的重试，超过重试就会结束请求\n\t\t\tswitch resp.StatusCode {\n\t\t\tdefault:\n\t\t\t\treturn 0, err\n\t\t\tcase http.StatusTooManyRequests:\n\t\t\tcase http.StatusBadGateway:\n\t\t\tcase http.StatusServiceUnavailable:\n\t\t\tcase http.StatusGatewayTimeout:\n\t\t\t}\n\t\t\t<-time.After(time.Millisecond * 200)\n\t\t\treturn 0, &errNeedRetry{err: fmt.Errorf(\"http request failure,status: %d\", resp.StatusCode)}\n\t\t}\n\n\t\t// 来到这 说明第1个分片下载 连接成功了\n\t\t// 后续分片仅在限流/网关类错误时当超载处理，其余错误（如链接过期 403）直接失败\n\t\tswitch resp.StatusCode {\n\t\tcase http.StatusTooManyRequests, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout:\n\t\tdefault:\n\t\t\treturn 0, err\n\t\t}\n\t\tlog.Debugf(\"err chunk_%d, try downloading:%v\", ch.id, err)\n\n\t\td.m.Lock()\n\t\tisCancelConcurrency := ch.newConcurrency\n\t\tif d.concurrency > 0 { // 取消剩余的并发任务\n\t\t\t// 用于计算实际的并发数\n\t\t\td.concurrency = -d.concurrency\n\t\t\tisCancelConcurrency = true\n\t\t}","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/internal/net/request.go#L396-L432","documentation":"Produced in the chunk response handler when the FIRST chunk (id 0) gets an HTTP status in the retryable set (429 TooManyRequests, 502, 503, 504). The first chunk gates the whole request, so it is wrapped in errNeedRetry with a 200ms pause, letting the outer loop re-establish the connection. Note the 'default' branch returns the raw err — the message only appears for those four statuses.","triggerScenarios":"Initial ranged GET for chunk 0 answered with 429/502/503/504 (throttling or gateway problems); repeated until the caller's retry budget is exhausted.","commonSituations":"Remote drive throttling new connections; frps/CDN gateway hiccup; service briefly down for maintenance; too many simultaneous download sessions from one IP.","solutions":["Retry after a delay — the condition is usually transient (that is why it is wrapped in errNeedRetry).","Lower download concurrency to avoid tripping rate limits.","If persistent, verify the remote service is healthy and the auth/URL has not expired.","Check the status code in the message to distinguish throttling (429) from gateway failures (502/503/504)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"var retryable *errNeedRetry\nif errors.As(err, &retryable) { /* retryable */ }","tryCatchPattern":"n, err := d.downloadChunk(...)\nif e, ok := err.(*errNeedRetry); ok {\n    log.Warnf(\"retryable: %v\", e)\n    continue // outer loop already sleeps 200ms for chunk 0\n}","preventionTips":["Honor errNeedRetry as retryable instead of failing the transfer.","Add jitter to retries when many clients share the same rate-limited endpoint.","Monitor which status dominates (429 vs 502/503/504) to pick the right fix."],"tags":["network","download","retry","http-status","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}