{"record":{"id":"77f78ed5b2853940","repo":"AlistGo/alist","slug":"upload-failed-after-d-retries-due-to-server-error-77f78e","errorCode":null,"errorMessage":"upload failed after %d retries due to server errors","messagePattern":"upload failed after (.+?) retries due to server errors","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/cloudreve_v4/util.go","lineNumber":425,"sourceCode":"\t\t}\n\t\treq, err := http.NewRequest(http.MethodPut, u.UploadUrls[chunk],\n\t\t\tdriver.NewLimitedUploadStream(ctx, bytes.NewBuffer(byteData)))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treq = req.WithContext(ctx)\n\t\treq.ContentLength = byteSize\n\t\tres, err := base.HttpClient.Do(req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tetag := res.Header.Get(\"ETag\")\n\t\tres.Body.Close()\n\t\tswitch {\n\t\tcase res.StatusCode != 200:\n\t\t\tretryCount++\n\t\t\tif retryCount > maxRetries {\n\t\t\t\treturn fmt.Errorf(\"upload failed after %d retries due to server errors\", maxRetries)\n\t\t\t}\n\t\t\tbackoff := time.Duration(1<<retryCount) * time.Second\n\t\t\tutils.Log.Warnf(\"server error %d, retrying after %v...\", res.StatusCode, backoff)\n\t\t\ttime.Sleep(backoff)\n\t\tcase etag == \"\":\n\t\t\treturn errors.New(\"faild to get ETag from header\")\n\t\tdefault:\n\t\t\tretryCount = 0\n\t\t\tetags = append(etags, etag)\n\t\t\tfinish += byteSize\n\t\t\tup(float64(finish) * 100 / float64(file.GetSize()))\n\t\t\tchunk++\n\t\t}\n\t}\n\n\t// s3LikeFinishUpload\n\tbodyBuilder := &strings.Builder{}\n\tbodyBuilder.WriteString(\"<CompleteMultipartUpload>\")","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/cloudreve_v4/util.go#L407-L443","documentation":"Fatal retry exhaustion in Cloudreve v4's S3-mediated upload (upS3). Every chunk PUT that does not return 200 is retried with exponential backoff — including 4xx responses — and after maxRetries consecutive non-200s the upload aborts with this message (no status detail is embedded, unlike the v3 variant). A 200 missing the ETag header fails separately.","triggerScenarios":"Each chunk PUT to S3 returns non-200 more than maxRetries times: expired/invalid multipart session (404), bad credentials or signature skew (403), or real S3 5xx. Because 4xx is retried, deterministic auth failures also exhaust retries and produce this bare message.","commonSituations":"Multipart session garbage-collected mid-upload so every part 404s; server clock drift causing 403s; S3-compatible backends rejecting parts; very large uploads where retries hide a permanent error until exhaustion.","solutions":["Check the Cloudreve v4 server and S3 backend logs to identify the actual status being retried (this message omits it)","Restart the upload for a fresh multipart session and verify S3 credentials on the server","Sync server time via NTP to eliminate 403 signature failures","Retry during a stable window; reduce chunk size or raise maxRetries if errors are transient"],"exampleFix":"// optional patch: include the status in the terminal error\n// before\nreturn fmt.Errorf(\"upload failed after %d retries due to server errors\", maxRetries)\n// after\nreturn fmt.Errorf(\"upload failed after %d retries due to server errors, error %d\", maxRetries, res.StatusCode)","handlingStrategy":"retry","validationCode":"// pre-flight: validate S3 session and part sizing before upload starts\nif chunkSize < 5*1024*1024 {\n    log.Warn(\"S3 parts below 5MiB minimum may fail complete-multipart-upload\")\n}","typeGuard":null,"tryCatchPattern":"if err := d.upS3(ctx, file, u, up); err != nil {\n    if strings.Contains(err.Error(), \"upload failed after\") {\n        // this message omits the status; check server logs, assume session/auth first,\n        // restart with a fresh session once, then surface the failure\n    }\n}","preventionTips":["Restart the upload (fresh multipart session) after retry exhaustion; do not extend blindly","Keep non-final parts at or above 5 MiB","Patch callers to log the status code alongside this error since it is not embedded"],"tags":["alist","cloudreve-v4","s3","upload","retry","multipart"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}