{"record":{"id":"6941cfc2531bb02f","repo":"AlistGo/alist","slug":"wukong-multipart-finish-failed-code-d-message-s","errorCode":null,"errorMessage":"wukong multipart finish failed: code=%d message=%s","messagePattern":"wukong multipart finish failed: code=(.+?) message=(.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/wukong/driver.go","lineNumber":722,"sourceCode":"\t\tSetHeader(\"Origin\", \"https://pan.wkbrowser.com\").\n\t\tSetHeader(\"Authorization\", auth).\n\t\tSetQueryParams(map[string]string{\n\t\t\t\"uploadid\":   uploadID,\n\t\t\t\"phase\":      \"finish\",\n\t\t\t\"uploadmode\": \"part\",\n\t\t}).\n\t\tSetBody(body).\n\t\tSetResult(&resp)\n\tif storageUser != \"\" {\n\t\treq.SetHeader(\"X-Storage-U\", storageUser)\n\t}\n\tuploadURL := fmt.Sprintf(\"https://%s/upload/v1/%s\", host, storeURI)\n\t_, err := req.Post(uploadURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resp.Code != minUploadSubmitSuccess && resp.Code != 4024 {\n\t\treturn fmt.Errorf(\"wukong multipart finish failed: code=%d message=%s\", resp.Code, resp.Message)\n\t}\n\treturn nil\n}\n\nfunc (d *Wukong) vodRequest(ctx context.Context, method string, query map[string]string, body []byte, auth *uploadAuthTokenResp, resp any) error {\n\treqURL := vodBaseURL + \"/\"\n\tamzDate := time.Now().UTC().Format(\"20060102T150405Z\")\n\tdateStamp := amzDate[:8]\n\theaders := map[string]string{\n\t\t\"x-amz-date\":           amzDate,\n\t\t\"x-amz-security-token\": auth.SessionToken,\n\t}\n\tif method == http.MethodPost {\n\t\theaders[\"x-amz-content-sha256\"] = hashSHA256Bytes(body)\n\t}\n\tauthorization := buildVodAuthorization(method, \"/\", query, headers, body, auth, dateStamp)\n\n\treq := base.NewRestyClient().R().","sourceCodeStart":704,"sourceCodeEnd":740,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/wukong/driver.go#L704-L740","documentation":"Thrown by finishMultipartUpload when the phase=finish POST returns a code that is neither 2000 (success) nor 4024 (tolerated — typically 'already finished/duplicate'). Finishing stitches the uploaded parts into the final object; failure here wastes the whole multipart session.","triggerScenarios":"Part list body (\"num:crc\" joined string) malformed or referencing a missing part; auth expired by the time the last part finished; uploadID already aborted; upstream error assembling parts.","commonSituations":"A part failed earlier and the code path still attempted finish; very large files where finish exceeds token TTL; retrying finish after a timeout that actually succeeded server-side (hence 4024 tolerance).","solutions":["Only call finish after every part uploaded successfully and its CRC matched (parts slice complete)","Retry finish once — transient assembly errors are common for huge objects","If the code suggests the session was already finished, proceed to uploadSubmit instead of erroring (mirror the 4024 tolerance)","Re-mint auth token before finish for long-running multipart uploads"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := d.finishMultipartUpload(ctx, host, storeURI, auth, storageUser, uploadID, partsBody); err != nil {\n    if isWukongTOSCode(err, 4024) {\n        return nil // already finished (driver already tolerates, keep for safety)\n    }\n    time.Sleep(2 * time.Second)\n    return d.finishMultipartUpload(ctx, host, storeURI, auth, storageUser, uploadID, partsBody)\n}","preventionTips":["Only finish when len(parts) == totalParts and every CRC matched","Keep the parts list body strictly 'num:crc' comma-joined with ascending part numbers","Mint a fresh token before finish on long uploads"],"tags":["wukong","upload","multipart","tos","finish","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}