{"record":{"id":"f1e74f9d7d9e6e00","repo":"juicedata/juicefs","slug":"part-d-s","errorCode":null,"errorMessage":"part %d: %s","messagePattern":"part (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/sync/sync.go","lineNumber":832,"sourceCode":"\t\tif obj, ok := dst.(object.SupportUploadPartStream); ok {\n\t\t\tpart, err = obj.UploadPartStream(key, uploadID, num+1, pr)\n\t\t}\n\n\t\tif errors.Is(err, utils.ErrNotSUP) {\n\t\t\tdata := dynAlloc(int(size))\n\t\t\tdefer dynFree(data)\n\t\t\tif _, err = io.ReadFull(pr, data); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t// PartNumber starts from 1\n\t\t\tpart, err = dst.UploadPart(ctx, key, uploadID, num+1, data)\n\t\t}\n\t\tchksum = r.chksum\n\t\treturn err\n\t})\n\tif err != nil {\n\t\tlogger.Warnf(\"Failed to copy data of %s part %d: %s\", key, num, err)\n\t\treturn nil, 0, fmt.Errorf(\"part %d: %s\", num, err)\n\t}\n\tlogger.Debugf(\"Copied data of %s part %d in %s\", key, num, time.Since(start))\n\treturn part, chksum, nil\n}\n\nfunc choosePartSize(upload *object.MultipartUpload, size int64) int64 {\n\tpartSize := int64(upload.MinPartSize)\n\tif partSize == 0 {\n\t\tpartSize = defaultPartSize\n\t}\n\tif size > partSize*int64(upload.MaxCount) {\n\t\tpartSize = size / int64(upload.MaxCount)\n\t\tpartSize = ((partSize-1)>>20 + 1) << 20 // align to MB\n\t}\n\treturn partSize\n}\n\nfunc doCopyRange(src, dst object.ObjectStorage, key string, off, size int64, upload *object.MultipartUpload, num int, abort chan struct{}, calChksum bool) (*object.Part, uint32, error) {","sourceCodeStart":814,"sourceCodeEnd":850,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/sync/sync.go#L814-L850","documentation":"doUploadPart wraps any failure of a single multipart part copy as 'part %d: %s' after logging 'Failed to copy data of %s part %d'. The inner error (network, abort, permission, size errors from src.Get/dst.UploadPart) is embedded; the caller aborts the whole multipart upload.","triggerScenarios":"UploadPart to dst fails (throttling, expired upload ID, permission), src range read fails, or abort fires while uploading one part during doCopyRange's multipart copy.","commonSituations":"Large objects with many parts where one part hits a 429/503; multipart upload expired on the backend; credentials lacking multipart permissions; user cancellation mid-part.","solutions":["Read the embedded inner error for the real cause (429/5xx -> retry; 404 NoSuchUpload -> restart the whole multipart upload).","The multipart upload is aborted by the caller; simply re-running the sync restarts it cleanly.","Lower --threads or increase part size to reduce throttling on big objects.","Verify dst credentials include multipart:UploadPart/AbortMultipartUpload."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// ensure dst supports multipart before large copies\nif size > 5*1024*1024 {\n\tif _, err := dst.CreateMultipartUpload(ctx, probeKey); err != nil {\n\t\treturn fmt.Errorf(\"multipart unsupported/denied on dst: %w\", err)\n\t}\n}","typeGuard":null,"tryCatchPattern":"part, chksum, err := doUploadPart(...)\nif err != nil {\n\t// caller already aborts the upload; classify for retry policy\n\tif isTransient(err) { // 429/5xx\n\t\treturn retryRangeCopyWithBackoff()\n\t}\n\treturn fmt.Errorf(\"part %d: %w\", num, err)\n}","preventionTips":["Reduce --threads or enlarge part size to avoid throttling on big objects","Confirm dst IAM includes UploadPart/AbortMultipartUpload/CompleteMultipartUpload","Restart expired uploads (NoSuchUpload means the upload handle aged out)","Keep idempotent re-sync so aborted multipart jobs can be replayed"],"tags":["multipart-upload","object-storage","retry"],"backgroundTag":"api-request-failed","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}