juicedata/juicefs · error
multipart: %s
Error message
multipart: %s
What it means
Wraps a failure of CompleteUpload after all parts of a multipart copy were uploaded. When dst.CompleteUpload fails 3 times (via try), the multipart upload is aborted and the error is reported as 'multipart: <err>'. The upload produced no object; the caller must retry the whole copy.
Source
Thrown at pkg/sync/sync.go:913
parts[i], chksum, err = doUploadPart(src, dst, key, off+int64(i)*partSize, sz, tmpkey, up.UploadID, i, calChksum)
if err != nil {
dst.AbortUpload(ctx, tmpkey, up.UploadID)
return nil, 0, fmt.Errorf("range(%d,%d): %s", off, size, err)
}
if calChksum {
if first {
tmpChksum = chksum
first = false
} else {
tmpChksum = crc32combine.CRC32Combine(crc32.Castagnoli, tmpChksum, chksum, sz)
}
}
}
err = try(3, func() error { return dst.CompleteUpload(ctx, tmpkey, up.UploadID, parts) })
if err != nil {
dst.AbortUpload(ctx, tmpkey, up.UploadID)
return nil, 0, fmt.Errorf("multipart: %s", err)
}
var part *object.Part
err = try(3, func() error {
part, err = dst.UploadPartCopy(ctx, key, upload.UploadID, num+1, tmpkey, 0, size)
return err
})
_ = dst.Delete(ctx, tmpkey)
return part, tmpChksum, err
}
func doCopyMultiple(src, dst object.ObjectStorage, key string, size int64, mtime time.Time, upload *object.MultipartUpload, calChksum bool, uploads multipartUploads) (uint32, error) {
limits := dst.Limits()
if size > limits.MaxPartSize*int64(upload.MaxCount) {
return 0, fmt.Errorf("object size %d is too large to copy", size)
}
partSize := choosePartSize(upload, size)
n := int((size-1)/partSize) + 1
logger.Debugf("Copying data of %s as %d parts (size: %d): %s", key, n, partSize, upload.UploadID)View on GitHub (pinned to c9a67b23e8)
Solutions
- Check the wrapped error for expired/aborted upload ID and rerun sync — a fresh upload will be started automatically.
- Remove or extend S3 'abort incomplete multipart upload' lifecycle rules on the destination bucket for large copies.
- Verify part ETags/validity: avoid concurrent writers to the same destination key.
- Retry during off-peak hours or with fewer --threads if the destination rejects the complete call under load.
Defensive patterns
Strategy: retry
Validate before calling
// ensure no lifecycle rule aborts incomplete multipart uploads too early
rules := getS3LifecycleRules(dstBucket)
for _, r := range rules {
if r.AbortIncompleteMultipartUpload != nil && r.AbortIncompleteMultipartUpload.DaysAfterInitiation < expectedUploadHours*24 {
return fmt.Errorf("lifecycle rule would abort upload")
}
} Try / catch
if err := sync(...); err != nil {
if strings.HasPrefix(err.Error(), "multipart:") {
logger.Warnf("multipart complete failed, retrying sync: %v", err)
return retryWithBackoff(sync, 3)
}
return err
} Prevention
- Disable/extend 'abort incomplete multipart upload' lifecycle rules on the destination for long copies.
- Make syncs exclusive per key; avoid concurrent writers to the same destination object.
- Keep upload sessions short enough to finish before destination upload-ID expiry.
When it happens
Trigger: dst.CompleteUpload(ctx, tmpkey, up.UploadID, parts) returns an error (retried 3 times) — typically because one or more uploaded parts are missing/invalid (ETag mismatch), the upload ID was expired, or the destination rejected the complete call.
Common situations: Long uploads where the destination expired the multipart upload ID (e.g. S3 lifecycle rule aborting incomplete multipart uploads); part list mismatch after a retried/aborted part; destination service outage during CompleteUpload.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- ibmcos: CreateMultipartUpload returned empty UploadId for %s
- ks3: CreateMultipartUpload returned empty UploadID for %s
- No ETag
- closed
- errDirSuffix
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/e814b829219a4ee8.
Report an issue: GitHub.