AlistGo/alist · critical

multipart part crc32 mismatch: part=%d local=%s remote=%s

Error message

multipart part crc32 mismatch: part=%d local=%s remote=%s

What it means

Thrown in uploadToTOSMultipart when the CRC32 returned for an individual part by uploadMultipartPart differs from the locally computed CRC of the bytes in buf. Only parts whose remote CRC is non-empty are checked. Like 1091, this is an end-to-end integrity mismatch but scoped to one part number of a multipart session.

Source

Thrown at drivers/wukong/driver.go:627

		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 {
			return err
		}
		if remoteCRC32 != "" && !strings.EqualFold(remoteCRC32, crc32Hex) {
			return fmt.Errorf("multipart part crc32 mismatch: part=%d local=%s remote=%s", partNumber, crc32Hex, remoteCRC32)
		}
		parts = append(parts, fmt.Sprintf("%d:%s", partNumber, crc32Hex))
		up(30 + float64(partNumber)/float64(totalParts)*50)
	}

	return d.finishMultipartUpload(ctx, host, storeURI, auth, storageUser, uploadID, strings.Join(parts, ","))
}

func (d *Wukong) initMultipartUpload(ctx context.Context, host, storeURI, auth, storageUser string) (string, error) {
	var resp tosUploadResp
	req := base.NewRestyClient().R().
		SetContext(ctx).
		SetHeader("Host", host).
		SetHeader("Referer", webReferer).
		SetHeader("Origin", "https://pan.wkbrowser.com").
		SetHeader("Authorization", auth).
		SetQueryParams(map[string]string{
			"uploadmode": "part",

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Re-upload only the mismatched part with the same uploadID and partNumber before finishing
  2. Ensure the temp file is immutable during the upload (snapshot/lock before multipart starts)
  3. On retry, always send the freshly read bytes at the part's fixed offset, never a stale buffer
  4. If mismatches are frequent, reduce part size or check host disk/NIC health

Example fix

// before
if remoteCRC32 != "" && !strings.EqualFold(remoteCRC32, crc32Hex) {
    return fmt.Errorf("multipart part crc32 mismatch: ...")
}

// after
for attempt := 0; attempt < 3; attempt++ {
    buf2 := readPart(tempFile, offset, partSize) // fresh read
    remoteCRC32, err = d.uploadMultipartPart(ctx, host, storeURI, auth, storageUser, uploadID, partNumber, buf2, crc32Hex)
    if err == nil && (remoteCRC32 == "" || strings.EqualFold(remoteCRC32, crc32Hex)) {
        break
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure temp file is fully cached and stable before multipart
tempFile, err := file.CacheFullInTempFile()
if err != nil { return err }
if fi, err := tempFile.(*os.File).Stat(); err == nil && fi.Size() != file.GetSize() {
    return errors.New("temp cache size mismatch; aborting multipart")
}

Try / catch

if remoteCRC32 != "" && !strings.EqualFold(remoteCRC32, crc32Hex) {
    // retry just this part once with freshly read bytes
    buf = mustReadAt(tempFile, offset, partSize)
    crc32Hex = fmt.Sprintf("%08x", crc32.ChecksumIEEE(buf))
    remoteCRC32, err = d.uploadMultipartPart(ctx, host, storeURI, auth, storageUser, uploadID, partNumber, buf, crc32Hex)
    if err != nil { return err }
    if remoteCRC32 != "" && !strings.EqualFold(remoteCRC32, crc32Hex) {
        return fmt.Errorf("multipart part crc32 mismatch persists: part=%d", partNumber)
    }
}

Prevention

When it happens

Trigger: Reading a part from the temp file with ReadAt while the file changed (offset now maps to different bytes); part body altered in transit; server storing a retried part under the wrong number; bug where partNumber was reused after a failed attempt.

Common situations: Source file modified during upload (no snapshot); parallel part uploads reusing buffers incorrectly; flaky network plus non-idempotent retry logic.

Related errors


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