juicedata/juicefs · error

CompleteMultipart: %s

Error message

CompleteMultipart: %s

What it means

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.

Source

Thrown at pkg/object/ufile.go:320

	return &Part{Num: num, Size: len(data), ETag: strings.Trim(etags[0], "\"")}, nil
}

func (u *ufile) AbortUpload(ctx context.Context, key string, uploadID string) {
	_, _ = u.request(ctx, "DELETE", key+"?uploads="+uploadID, nil, nil)
}

func (u *ufile) CompleteUpload(ctx context.Context, key string, uploadID string, parts []*Part) error {
	etags := make([]string, len(parts))
	for i, p := range parts {
		etags[i] = p.ETag
	}
	resp, err := u.request(ctx, "POST", key+"?uploadId="+uploadID, bytes.NewReader([]byte(strings.Join(etags, ","))), nil)
	if err != nil {
		return err
	}
	defer cleanup(resp)
	if resp.StatusCode != 200 {
		return fmt.Errorf("CompleteMultipart: %s", parseError(resp).Error())
	}
	return nil
}

type ufileUpload struct {
	FileName  string
	UploadId  string
	StartTime int
}

type ufileListMultipartUploadsResult struct {
	RetCode    int
	ErrMsg     string
	NextMarker string
	DataSet    []*ufileUpload
}

func (u *ufile) ListUploads(ctx context.Context, marker string) ([]*PendingPart, string, error) {

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Ensure all parts uploaded successfully and ETags were collected correctly
  2. Verify uploadId is still valid; restart the multipart upload if expired
  3. Check the parsed message body for the UFile error code
  4. Clean up stale parts via ListUploads/abort if the upload cannot be completed

Example fix

// before
// complete even when a part failed, etags empty
u.CompleteUpload(ctx, key, uploadID, etags)
// after
// only complete when all parts have ETags
if len(etags) != totalParts { return errors.New("missing parts") }
u.CompleteUpload(ctx, key, uploadID, etags)
Defensive patterns

Strategy: try-catch

Validate before calling

if len(etags) != expectedParts { return errors.New("incomplete part list before CompleteUpload") }

Try / catch

err := store.CompleteUpload(ctx, key, uploadID, etags)
if err != nil && strings.HasPrefix(err.Error(), "CompleteMultipart:") {
	// inspect UFile error body; restart upload if uploadId expired
}

Prevention

When it happens

Trigger: Calling CompleteUpload with an invalid/expired uploadId, mismatched or reordered ETags, incomplete part set, or UFile returning 4xx/5xx on the completion POST.

Common situations: Upload abandoned and uploadId expired server-side; some parts failed but completion was attempted anyway; signature/keys wrong; etag list built from failed parts.

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


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/6865060d41e54f4d. Report an issue: GitHub.