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

  1. Check the Cloudreve v4 server and S3 backend logs to identify the actual status being retried (this message omits it)
  2. Restart the upload for a fresh multipart session and verify S3 credentials on the server
  3. Sync server time via NTP to eliminate 403 signature failures
  4. 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

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


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/77f78ed5b2853940. Report an issue: GitHub.