juicedata/juicefs · error

UploadPart: %s

Error message

UploadPart: %s

What it means

Documentation placeholder for ufile UploadPart errors: the PUT request uploading one multipart part to UFile failed; UFile additionally requires part numbers to start from zero and be continuous, which the client adjusts for before this request.

Source

Thrown at pkg/object/ufile.go:296

	}
	var out ufileCreateMultipartUploadResult
	if err := u.parseResp(resp, &out); err != nil {
		return nil, err
	}
	return &MultipartUpload{UploadID: out.UploadId, MinPartSize: int64(out.BlkSize), MaxCount: 1000000}, nil
}

func (u *ufile) UploadPart(ctx context.Context, key string, uploadID string, num int, data []byte) (*Part, error) {
	// UFile require the PartNumber to start from 0 (continuous)
	num--
	path := fmt.Sprintf("%s?uploadId=%s&partNumber=%d", key, uploadID, num)
	resp, err := u.request(ctx, "PUT", path, bytes.NewReader(data), nil)
	if err != nil {
		return nil, err
	}
	defer cleanup(resp)
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("UploadPart: %s", parseError(resp).Error())
	}
	etags := resp.Header["Etag"]
	if len(etags) < 1 {
		return nil, errors.New("No ETag")
	}
	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)

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Check the wrapped parseError message for the actual UFile error code
  2. Ensure non-final parts meet UFile's minimum part size (e.g. 4 MiB)
  3. Verify access/secret keys and bucket binding
  4. Retry the failed part; if ETag missing, check intermediaries/proxies stripping headers

Example fix

// before
u.UploadPart(ctx, key, uploadID, num, 1<<20) // 1MB part
// after
u.UploadPart(ctx, key, uploadID, num, 8<<20) // >= 4MB part
Defensive patterns

Strategy: try-catch

Validate before calling

func validPartSize(sz int) error { if sz < 4<<20 { return errors.New("part too small for UFile") }; return nil }

Try / catch

p, err := store.UploadPart(ctx, key, uploadID, num, data)
if err != nil && strings.HasPrefix(err.Error(), "UploadPart:") {
	// backoff and retry this single part
}

Prevention

When it happens

Trigger: PUT of a part to UFile fails: signature error, part size below minimum (UFile requires parts >4MB except last), quota exceeded, network drop producing 5xx, or server omits the Etag header.

Common situations: Uploading large files where middle parts are too small; wrong secret key; UFile bucket storage class rejecting the upload; proxy stripping the Etag header.

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/8145dc6a3f03e04a. Report an issue: GitHub.