AlistGo/alist · warning

invalid multipart parts

Error message

invalid multipart parts

What it means

Thrown by uploadToTOSMultipart when the computed part count is <= 0. totalParts = ceil(size / multipartChunkSize); the function is only called when size > multipartChunkSize, which forces totalParts >= 1, so in the current call graph this is a defensive guard against a non-positive size rather than a reachable condition. Seeing it means the function was invoked with size <= 0 (e.g. via refactoring or a direct call), or multipartChunkSize became <= 0/overflowed.

Source

Thrown at drivers/wukong/driver.go:605

	}
	if resp.Code != minUploadSubmitSuccess {
		return fmt.Errorf("wukong upload to tos failed: code=%d message=%s", resp.Code, resp.Message)
	}
	if resp.Data.Crc32 != "" && !strings.EqualFold(resp.Data.Crc32, crc32Hex) {
		return fmt.Errorf("wukong upload to tos crc32 mismatch: local=%s remote=%s", crc32Hex, resp.Data.Crc32)
	}
	return nil
}

func (d *Wukong) uploadToTOSMultipart(ctx context.Context, host, storeURI, auth, storageUser string, tempFile model.File, size int64, up driver.UpdateProgress) error {
	uploadID, err := d.initMultipartUpload(ctx, host, storeURI, auth, storageUser)
	if err != nil {
		return err
	}

	totalParts := int((size + multipartChunkSize - 1) / multipartChunkSize)
	if totalParts <= 0 {
		return errors.New("invalid multipart parts")
	}
	parts := make([]string, 0, totalParts)
	for i := 0; i < totalParts; i++ {
		partNumber := i + 1
		offset := int64(i) * multipartChunkSize
		partSize := multipartChunkSize
		if remain := size - offset; remain < partSize {
			partSize = remain
		}
		buf := make([]byte, partSize)
		n, readErr := tempFile.ReadAt(buf, offset)
		if readErr != nil && readErr != io.EOF {
			return readErr
		}
		buf = buf[:n]
		crc32Hex := fmt.Sprintf("%08x", crc32.ChecksumIEEE(buf))
		remoteCRC32, err := d.uploadMultipartPart(ctx, host, storeURI, auth, storageUser, uploadID, partNumber, buf, crc32Hex)
		if err != nil {

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Route uploads through Put()/uploadToTOS so the size > multipartChunkSize precondition holds
  2. If calling the helper directly, verify size > multipartChunkSize first
  3. For 0-byte files, ensure the single-shot (non-multipart) branch is taken

Example fix

// before
err := d.uploadToTOSMultipart(ctx, host, uri, auth, uid, tempFile, size, up)

// after
if size > multipartChunkSize {
    err = d.uploadToTOSMultipart(ctx, host, uri, auth, uid, tempFile, size, up)
} else {
    err = d.uploadToTOSDirect(ctx, host, uri, auth, uid, tempFile, up)
}
Defensive patterns

Strategy: validation

Validate before calling

if size <= multipartChunkSize {
    return fmt.Errorf("size %d does not require multipart upload", size)
}
// now safe: totalParts = ceil(size/chunk) >= 1

Prevention

When it happens

Trigger: Direct invocation of uploadToTOSMultipart with a zero or negative size; a future code path that calls it for small files; chunk-size constant accidentally changed to 0 or a negative value.

Common situations: Driver forks or refactors that bypass the size > multipartChunkSize branch; uploading an empty (0-byte) file through a modified Put() that skips the size check; unit tests calling the helper directly.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/049eb0260d38723d. Report an issue: GitHub.