{"record":{"id":"6865060d41e54f4d","repo":"juicedata/juicefs","slug":"completemultipart-s","errorCode":null,"errorMessage":"CompleteMultipart: %s","messagePattern":"CompleteMultipart: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/object/ufile.go","lineNumber":320,"sourceCode":"\treturn &Part{Num: num, Size: len(data), ETag: strings.Trim(etags[0], \"\\\"\")}, nil\n}\n\nfunc (u *ufile) AbortUpload(ctx context.Context, key string, uploadID string) {\n\t_, _ = u.request(ctx, \"DELETE\", key+\"?uploads=\"+uploadID, nil, nil)\n}\n\nfunc (u *ufile) CompleteUpload(ctx context.Context, key string, uploadID string, parts []*Part) error {\n\tetags := make([]string, len(parts))\n\tfor i, p := range parts {\n\t\tetags[i] = p.ETag\n\t}\n\tresp, err := u.request(ctx, \"POST\", key+\"?uploadId=\"+uploadID, bytes.NewReader([]byte(strings.Join(etags, \",\"))), nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cleanup(resp)\n\tif resp.StatusCode != 200 {\n\t\treturn fmt.Errorf(\"CompleteMultipart: %s\", parseError(resp).Error())\n\t}\n\treturn nil\n}\n\ntype ufileUpload struct {\n\tFileName  string\n\tUploadId  string\n\tStartTime int\n}\n\ntype ufileListMultipartUploadsResult struct {\n\tRetCode    int\n\tErrMsg     string\n\tNextMarker string\n\tDataSet    []*ufileUpload\n}\n\nfunc (u *ufile) ListUploads(ctx context.Context, marker string) ([]*PendingPart, string, error) {","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/object/ufile.go#L302-L338","documentation":"CompleteUpload posts the joined ETag list for a multipart upload; a non-200 response is wrapped as \"CompleteMultipart: <parsed error>\". This finalizes the upload — failure means the file is not committed and parts may remain.","triggerScenarios":"Calling CompleteUpload with an invalid/expired uploadId, mismatched or reordered ETags, incomplete part set, or UFile returning 4xx/5xx on the completion POST.","commonSituations":"Upload abandoned and uploadId expired server-side; some parts failed but completion was attempted anyway; signature/keys wrong; etag list built from failed parts.","solutions":["Ensure all parts uploaded successfully and ETags were collected correctly","Verify uploadId is still valid; restart the multipart upload if expired","Check the parsed message body for the UFile error code","Clean up stale parts via ListUploads/abort if the upload cannot be completed"],"exampleFix":"// before\n// complete even when a part failed, etags empty\nu.CompleteUpload(ctx, key, uploadID, etags)\n// after\n// only complete when all parts have ETags\nif len(etags) != totalParts { return errors.New(\"missing parts\") }\nu.CompleteUpload(ctx, key, uploadID, etags)","handlingStrategy":"try-catch","validationCode":"if len(etags) != expectedParts { return errors.New(\"incomplete part list before CompleteUpload\") }","typeGuard":null,"tryCatchPattern":"err := store.CompleteUpload(ctx, key, uploadID, etags)\nif err != nil && strings.HasPrefix(err.Error(), \"CompleteMultipart:\") {\n\t// inspect UFile error body; restart upload if uploadId expired\n}","preventionTips":["Only complete after every part returned an ETag","Complete promptly — uploadIds expire server-side","Abort and clean up failed uploads to avoid orphaned parts"],"tags":["ufile","multipart-upload","http","object-storage"],"backgroundTag":"http-error-response","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}