AlistGo/alist · error

[doubao_new] finish upload invalid num_blocks

Error message

[doubao_new] finish upload invalid num_blocks

What it means

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.

Source

Thrown at drivers/doubao_new/util.go:879

	urlStr := "https://internal-api-drive-stream.feishu.cn/space/api/box/stream/upload/v3/block/?" + values.Encode()

	res, err := req.Execute(http.MethodPost, urlStr)
	if err != nil {
		return err
	}
	body := res.Body()
	if err := decodeBaseResp(body, res); err != nil {
		return err
	}
	return nil
}

func (d *DoubaoNew) finishUpload(ctx context.Context, uploadID string, numBlocks int, mountPoint string) (UploadFinishData, error) {
	if uploadID == "" {
		return UploadFinishData{}, fmt.Errorf("[doubao_new] finish upload missing upload_id")
	}
	if numBlocks <= 0 {
		return UploadFinishData{}, fmt.Errorf("[doubao_new] finish upload invalid num_blocks")
	}
	if mountPoint == "" {
		mountPoint = "explorer"
	}
	var resp UploadFinishResp
	_, err := d.request(ctx, "/space/api/box/upload/finish/", http.MethodPost, func(req *resty.Request) {
		values := url.Values{}
		values.Set("shouldBypassScsDialog", "true")
		values.Set("doubao_storage", "imagex_other")
		values.Set("doubao_app_id", "497858")
		req.SetQueryParamsFromValues(values)
		req.SetHeader("Content-Type", "application/json")
		req.SetHeader("x-command", "space.api.box.upload.finish")
		req.SetHeader("rpc-persist-doubao-pan", "true")
		req.SetHeader("cache-control", "no-cache")
		req.SetHeader("pragma", "no-cache")
		req.SetHeader("biz-scene", "file_upload")
		req.SetHeader("biz-ua-type", "Web")

View on GitHub (pinned to 843d9dc814)

Solutions

  1. For empty files use the direct/empty-file upload path rather than block upload + finish.
  2. Count all blocks reported by prepare (uploaded + newly uploaded), not only the ones transferred in this session.
  3. Abort on the first block-upload error so finish is never called with a partial count.

Example fix

// before
d.finishUpload(ctx, uploadID, len(neededUploadBlocks), mountPoint)

// after
if len(blocks) == 0 { return UploadFinishData{}, fmt.Errorf("[doubao_new] no blocks to finish") }
d.finishUpload(ctx, uploadID, len(blocks), mountPoint)
Defensive patterns

Strategy: validation

Validate before calling

if len(blocks) == 0 { return fmt.Errorf("no blocks recorded; refusing to finish") }
data, err := d.finishUpload(ctx, uploadID, len(blocks), mountPoint)

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/dc558031319f41dd. Report an issue: GitHub.