AlistGo/alist · error
upload failed after %d retries due to server errors
Error message
upload failed after %d retries due to server errors
What it means
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.
Source
Thrown at drivers/cloudreve_v4/util.go:425
}
req, err := http.NewRequest(http.MethodPut, u.UploadUrls[chunk],
driver.NewLimitedUploadStream(ctx, bytes.NewBuffer(byteData)))
if err != nil {
return err
}
req = req.WithContext(ctx)
req.ContentLength = byteSize
res, err := base.HttpClient.Do(req)
if err != nil {
return err
}
etag := res.Header.Get("ETag")
res.Body.Close()
switch {
case res.StatusCode != 200:
retryCount++
if retryCount > maxRetries {
return fmt.Errorf("upload failed after %d retries due to server errors", maxRetries)
}
backoff := time.Duration(1<<retryCount) * time.Second
utils.Log.Warnf("server error %d, retrying after %v...", res.StatusCode, backoff)
time.Sleep(backoff)
case etag == "":
return errors.New("faild to get ETag from header")
default:
retryCount = 0
etags = append(etags, etag)
finish += byteSize
up(float64(finish) * 100 / float64(file.GetSize()))
chunk++
}
}
// s3LikeFinishUpload
bodyBuilder := &strings.Builder{}
bodyBuilder.WriteString("<CompleteMultipartUpload>")View on GitHub (pinned to 843d9dc814)
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
Example fix
// optional patch: include the status in the terminal error
// before
return fmt.Errorf("upload failed after %d retries due to server errors", maxRetries)
// after
return fmt.Errorf("upload failed after %d retries due to server errors, error %d", maxRetries, res.StatusCode) Defensive patterns
Strategy: retry
Validate before calling
// pre-flight: validate S3 session and part sizing before upload starts
if chunkSize < 5*1024*1024 {
log.Warn("S3 parts below 5MiB minimum may fail complete-multipart-upload")
} Try / catch
if err := d.upS3(ctx, file, u, up); err != nil {
if strings.Contains(err.Error(), "upload failed after") {
// this message omits the status; check server logs, assume session/auth first,
// restart with a fresh session once, then surface the failure
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- up status: %d, error: %s
- up status: %d, error: %s
- upload failed after %d retries due to server errors, error:
- upload failed after %d retries due to server errors, error %
- upload failed after %d retries due to server errors, error:
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/77f78ed5b2853940.
Report an issue: GitHub.