{"record":{"id":"c9c0c0604c84cbdb","repo":"AlistGo/alist","slug":"doubao-new-upload-v3-block-missing-upload-id","errorCode":null,"errorMessage":"[doubao_new] upload v3 block missing upload_id","messagePattern":"\\[doubao_new\\] upload v3 block missing upload_id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/doubao_new/util.go","lineNumber":821,"sourceCode":"\t\t\t\t}\n\t\t\t\tsuccess = append(success, seq)\n\t\t\t\toffset += int(size)\n\t\t\t}\n\t\t\treturn UploadMergeData{SuccessSeqList: success}, nil\n\t\t}\n\t\terrMsg := resp.Msg\n\t\tif errMsg == \"\" {\n\t\t\terrMsg = resp.Message\n\t\t}\n\t\treturn UploadMergeData{}, fmt.Errorf(\"[doubao_new] API error (code: %d): %s\", resp.Code, errMsg)\n\t}\n\n\treturn resp.Data, nil\n}\n\nfunc (d *DoubaoNew) uploadBlockV3(ctx context.Context, uploadID string, block UploadBlockNeed, data []byte) error {\n\tif uploadID == \"\" {\n\t\treturn fmt.Errorf(\"[doubao_new] upload v3 block missing upload_id\")\n\t}\n\tif block.Seq < 0 {\n\t\treturn fmt.Errorf(\"[doubao_new] upload v3 block invalid seq\")\n\t}\n\tif len(data) == 0 {\n\t\treturn fmt.Errorf(\"[doubao_new] upload v3 block empty data\")\n\t}\n\n\treq := base.RestyClient.R()\n\treq.SetContext(ctx)\n\treq.SetHeader(\"accept\", \"*/*\")\n\treq.SetHeader(\"origin\", \"https://www.doubao.com\")\n\treq.SetHeader(\"referer\", \"https://www.doubao.com/\")\n\treq.SetHeader(\"user-agent\", \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36\")\n\treq.SetHeader(\"rpc-persist-doubao-pan\", \"true\")\n\treq.SetHeader(\"x-block-seq\", strconv.Itoa(block.Seq))\n\treq.SetHeader(\"x-block-checksum\", block.Checksum)\n\tif auth := d.resolveAuthorization(); auth != \"\" {","sourceCodeStart":803,"sourceCodeEnd":839,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/doubao_new/util.go#L803-L839","documentation":"uploadBlockV3 (the per-block fallback re-upload path used after a 400/code-2 merge rejection) validates that uploadID is non-empty before building the request. An empty uploadID means the caller passed the identifier from a failed/uninitialized prepare.","triggerScenarios":"Calling uploadBlockV3 directly with \"\"; or the fallback loop receiving an uploadID that was emptied/never set because prepare failed softly. In stock code mergeUploadBlocks already validated uploadID, so this is a defensive guard mostly relevant to refactors and direct callers.","commonSituations":"Custom code extracting the v3 fallback into a retry helper and losing the uploadID parameter; prepare response missing the field after an API change.","solutions":["Ensure the uploadID from the prepare response is propagated verbatim into every blocks/merge/v3 call.","Fail fast right after prepare if UploadID is empty rather than continuing into block upload.","Do not retry on this error — it is deterministic until the prepare step is redone."],"exampleFix":"// before\nd.uploadBlockV3(ctx, \"\", block, payload)\n\n// after\nif uploadID == \"\" {\n    return fmt.Errorf(\"[doubao_new] cannot upload block: prepare returned no upload_id\")\n}\nd.uploadBlockV3(ctx, uploadID, block, payload)","handlingStrategy":"validation","validationCode":"if uploadID == \"\" {\n    return fmt.Errorf(\"cannot upload block: missing upload_id (re-run prepare)\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fail fast after prepare if upload_id is empty.","Propagate the uploadID as an immutable value through the upload chain."],"tags":["doubao","upload","validation"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}