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
- Re-upload only the mismatched part with the same uploadID and partNumber before finishing
- Ensure the temp file is immutable during the upload (snapshot/lock before multipart starts)
- On retry, always send the freshly read bytes at the part's fixed offset, never a stale buffer
- 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
- Snapshot/lock the source file for the duration of multipart upload
- Re-read part bytes from disk on retry, never reuse the buffer
- Retry at part granularity rather than aborting the whole session
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
- wukong upload to tos crc32 mismatch: local=%s remote=%s
- upload part failed: crc32 mismatch, expected %s, got %s
- wukong init multipart upload failed: code=%d message=%s
- wukong multipart transfer failed: code=%d message=%s part=%d
- wukong multipart finish failed: code=%d message=%s
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/78af5d1c15d2cfb5.
Report an issue: GitHub.