{"record":{"id":"78af5d1c15d2cfb5","repo":"AlistGo/alist","slug":"multipart-part-crc32-mismatch-part-d-local-s-re","errorCode":null,"errorMessage":"multipart part crc32 mismatch: part=%d local=%s remote=%s","messagePattern":"multipart part crc32 mismatch: part=(.+?) local=(.+?) remote=(.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"drivers/wukong/driver.go","lineNumber":627,"sourceCode":"\t\tpartNumber := i + 1\n\t\toffset := int64(i) * multipartChunkSize\n\t\tpartSize := multipartChunkSize\n\t\tif remain := size - offset; remain < partSize {\n\t\t\tpartSize = remain\n\t\t}\n\t\tbuf := make([]byte, partSize)\n\t\tn, readErr := tempFile.ReadAt(buf, offset)\n\t\tif readErr != nil && readErr != io.EOF {\n\t\t\treturn readErr\n\t\t}\n\t\tbuf = buf[:n]\n\t\tcrc32Hex := fmt.Sprintf(\"%08x\", crc32.ChecksumIEEE(buf))\n\t\tremoteCRC32, err := d.uploadMultipartPart(ctx, host, storeURI, auth, storageUser, uploadID, partNumber, buf, crc32Hex)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif remoteCRC32 != \"\" && !strings.EqualFold(remoteCRC32, crc32Hex) {\n\t\t\treturn fmt.Errorf(\"multipart part crc32 mismatch: part=%d local=%s remote=%s\", partNumber, crc32Hex, remoteCRC32)\n\t\t}\n\t\tparts = append(parts, fmt.Sprintf(\"%d:%s\", partNumber, crc32Hex))\n\t\tup(30 + float64(partNumber)/float64(totalParts)*50)\n\t}\n\n\treturn d.finishMultipartUpload(ctx, host, storeURI, auth, storageUser, uploadID, strings.Join(parts, \",\"))\n}\n\nfunc (d *Wukong) initMultipartUpload(ctx context.Context, host, storeURI, auth, storageUser string) (string, error) {\n\tvar resp tosUploadResp\n\treq := base.NewRestyClient().R().\n\t\tSetContext(ctx).\n\t\tSetHeader(\"Host\", host).\n\t\tSetHeader(\"Referer\", webReferer).\n\t\tSetHeader(\"Origin\", \"https://pan.wkbrowser.com\").\n\t\tSetHeader(\"Authorization\", auth).\n\t\tSetQueryParams(map[string]string{\n\t\t\t\"uploadmode\": \"part\",","sourceCodeStart":609,"sourceCodeEnd":645,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/wukong/driver.go#L609-L645","documentation":"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.","triggerScenarios":"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.","commonSituations":"Source file modified during upload (no snapshot); parallel part uploads reusing buffers incorrectly; flaky network plus non-idempotent retry logic.","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"],"exampleFix":"// before\nif remoteCRC32 != \"\" && !strings.EqualFold(remoteCRC32, crc32Hex) {\n    return fmt.Errorf(\"multipart part crc32 mismatch: ...\")\n}\n\n// after\nfor attempt := 0; attempt < 3; attempt++ {\n    buf2 := readPart(tempFile, offset, partSize) // fresh read\n    remoteCRC32, err = d.uploadMultipartPart(ctx, host, storeURI, auth, storageUser, uploadID, partNumber, buf2, crc32Hex)\n    if err == nil && (remoteCRC32 == \"\" || strings.EqualFold(remoteCRC32, crc32Hex)) {\n        break\n    }\n}","handlingStrategy":"retry","validationCode":"// ensure temp file is fully cached and stable before multipart\ntempFile, err := file.CacheFullInTempFile()\nif err != nil { return err }\nif fi, err := tempFile.(*os.File).Stat(); err == nil && fi.Size() != file.GetSize() {\n    return errors.New(\"temp cache size mismatch; aborting multipart\")\n}","typeGuard":null,"tryCatchPattern":"if remoteCRC32 != \"\" && !strings.EqualFold(remoteCRC32, crc32Hex) {\n    // retry just this part once with freshly read bytes\n    buf = mustReadAt(tempFile, offset, partSize)\n    crc32Hex = fmt.Sprintf(\"%08x\", crc32.ChecksumIEEE(buf))\n    remoteCRC32, err = d.uploadMultipartPart(ctx, host, storeURI, auth, storageUser, uploadID, partNumber, buf, crc32Hex)\n    if err != nil { return err }\n    if remoteCRC32 != \"\" && !strings.EqualFold(remoteCRC32, crc32Hex) {\n        return fmt.Errorf(\"multipart part crc32 mismatch persists: part=%d\", partNumber)\n    }\n}","preventionTips":["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"],"tags":["wukong","upload","multipart","crc32","integrity","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}