{"record":{"id":"dc558031319f41dd","repo":"AlistGo/alist","slug":"doubao-new-finish-upload-invalid-num-blocks","errorCode":null,"errorMessage":"[doubao_new] finish upload invalid num_blocks","messagePattern":"\\[doubao_new\\] finish upload invalid num_blocks","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/doubao_new/util.go","lineNumber":879,"sourceCode":"\turlStr := \"https://internal-api-drive-stream.feishu.cn/space/api/box/stream/upload/v3/block/?\" + values.Encode()\n\n\tres, err := req.Execute(http.MethodPost, urlStr)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody := res.Body()\n\tif err := decodeBaseResp(body, res); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (d *DoubaoNew) finishUpload(ctx context.Context, uploadID string, numBlocks int, mountPoint string) (UploadFinishData, error) {\n\tif uploadID == \"\" {\n\t\treturn UploadFinishData{}, fmt.Errorf(\"[doubao_new] finish upload missing upload_id\")\n\t}\n\tif numBlocks <= 0 {\n\t\treturn UploadFinishData{}, fmt.Errorf(\"[doubao_new] finish upload invalid num_blocks\")\n\t}\n\tif mountPoint == \"\" {\n\t\tmountPoint = \"explorer\"\n\t}\n\tvar resp UploadFinishResp\n\t_, err := d.request(ctx, \"/space/api/box/upload/finish/\", http.MethodPost, func(req *resty.Request) {\n\t\tvalues := url.Values{}\n\t\tvalues.Set(\"shouldBypassScsDialog\", \"true\")\n\t\tvalues.Set(\"doubao_storage\", \"imagex_other\")\n\t\tvalues.Set(\"doubao_app_id\", \"497858\")\n\t\treq.SetQueryParamsFromValues(values)\n\t\treq.SetHeader(\"Content-Type\", \"application/json\")\n\t\treq.SetHeader(\"x-command\", \"space.api.box.upload.finish\")\n\t\treq.SetHeader(\"rpc-persist-doubao-pan\", \"true\")\n\t\treq.SetHeader(\"cache-control\", \"no-cache\")\n\t\treq.SetHeader(\"pragma\", \"no-cache\")\n\t\treq.SetHeader(\"biz-scene\", \"file_upload\")\n\t\treq.SetHeader(\"biz-ua-type\", \"Web\")","sourceCodeStart":861,"sourceCodeEnd":897,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/doubao_new/util.go#L861-L897","documentation":"finishUpload requires numBlocks > 0 — the count of uploaded blocks sent to the finish endpoint. Zero or negative means the caller believes nothing was uploaded, which the API rejects; at minimum one block (or a direct upload record) must exist before finishing.","triggerScenarios":"Computing numBlocks from a block list that ended up empty (all blocks filtered out, zero-byte file, sizeList undercount) or passing len() of the wrong slice; finishing after every block upload failed but errors were ignored.","commonSituations":"Empty-file uploads; error-swallowing loops where failed block uploads don't abort; off-by-one when counting only 'needed' blocks instead of all blocks.","solutions":["For empty files use the direct/empty-file upload path rather than block upload + finish.","Count all blocks reported by prepare (uploaded + newly uploaded), not only the ones transferred in this session.","Abort on the first block-upload error so finish is never called with a partial count."],"exampleFix":"// before\nd.finishUpload(ctx, uploadID, len(neededUploadBlocks), mountPoint)\n\n// after\nif len(blocks) == 0 { return UploadFinishData{}, fmt.Errorf(\"[doubao_new] no blocks to finish\") }\nd.finishUpload(ctx, uploadID, len(blocks), mountPoint)","handlingStrategy":"validation","validationCode":"if len(blocks) == 0 { return fmt.Errorf(\"no blocks recorded; refusing to finish\") }\ndata, err := d.finishUpload(ctx, uploadID, len(blocks), mountPoint)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Count total blocks from prepare (pre-uploaded + new), not just this session's transfers.","Abort on first block-upload error."],"tags":["doubao","upload","validation"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}