juicedata/juicefs · error

ks3: UploadPartCopy returned no CopyPartResult for %s part %

Error message

ks3: UploadPartCopy returned no CopyPartResult for %s part %d

What it means

ks3.UploadPartCopy performs a server-side copy for one part of a multipart upload and requires resp.CopyPartResult to be present, since the part's ETag lives there. A 200 response with a nil CopyPartResult is treated as a failed copy and surfaces as "ks3: UploadPartCopy returned no CopyPartResult for %s part %d". Without the ETag the part cannot be validated in CompleteMultipartUpload.

Source

Thrown at pkg/object/ks3.go:316

		},
	})
	return err
}

func (s *ks3) UploadPartCopy(ctx context.Context, key string, uploadID string, num int, srcKey string, off, size int64) (*Part, error) {
	resp, err := s.s3.UploadPartCopyWithContext(ctx, &s3.UploadPartCopyInput{
		Bucket:          aws.String(s.bucket),
		CopySource:      aws.String(s.bucket + "/" + srcKey),
		CopySourceRange: aws.String(fmt.Sprintf("bytes=%d-%d", off, off+size-1)),
		Key:             aws.String(key),
		PartNumber:      aws.Long(int64(num)),
		UploadID:        aws.String(uploadID),
	})
	if err != nil {
		return nil, err
	}
	if resp.CopyPartResult == nil {
		return nil, fmt.Errorf("ks3: UploadPartCopy returned no CopyPartResult for %s part %d", key, num)
	}
	return &Part{Num: num, ETag: aws.ToString(resp.CopyPartResult.ETag)}, nil
}

func (s *ks3) AbortUpload(ctx context.Context, key string, uploadID string) {
	params := &s3.AbortMultipartUploadInput{
		Bucket:   &s.bucket,
		Key:      &key,
		UploadID: &uploadID,
	}
	_, _ = s.s3.AbortMultipartUploadWithContext(ctx, params)
}

func (s *ks3) CompleteUpload(ctx context.Context, key string, uploadID string, parts []*Part) error {
	var s3Parts []*s3.CompletedPart
	for i := range parts {
		n := new(int64)
		*n = int64(parts[i].Num)

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Verify the source key exists and is readable at copy time (retry the operation).
  2. Inspect the raw response with SDK debug logging to see whether CopyPartResult XML was returned but not parsed; update the SDK if so.
  3. Bypass UploadPartCopy by re-uploading the part data directly via UploadPart if the endpoint doesn't fully support copy.
  4. If using a proxy/compatible gateway, test against the genuine KS3 endpoint to isolate the culprit.

Example fix

// before
_, err := s.UploadPartCopy(ctx, dstKey, uploadID, num, srcKey, 0, size)  // nil CopyPartResult from proxy
// after
// read source and upload directly if copy is unsupported
part, err := s.UploadPart(ctx, dstKey, uploadID, num, data)
Defensive patterns

Strategy: try-catch

Validate before calling

if src, err := storage.Head(ctx, srcKey); err != nil || src == nil {
    return fmt.Errorf("source %s not available for UploadPartCopy", srcKey)
}

Try / catch

part, err := storage.UploadPartCopy(ctx, dstKey, uploadID, num, srcKey, off, size)
if err != nil {
    if strings.Contains(err.Error(), "no CopyPartResult") {
        // fall back to reading the source and uploading the part directly
        return uploadPartDirect(ctx, dstKey, uploadID, num, srcKey, off, size)
    }
    return err
}

Prevention

When it happens

Trigger: UploadPartCopy called where KS3 returns success but with a nil CopyPartResult — source object missing/unreadable server-side with error swallowed, response XML missing CopyPartResult due to a proxy, or SDK deserialization mismatch on the CopyPartResult field.

Common situations: Copying through a proxy that strips response elements; source object deleted between validation and copy; KS3-compatible endpoint not fully implementing UploadPartCopy; SDK version mismatch with the KS3 response format.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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