AlistGo/alist · error

[doubao_new] finish upload missing upload_id

Error message

[doubao_new] finish upload missing upload_id

What it means

finishUpload finalizes a completed upload session via POST /space/api/box/upload/finish/ and first validates that uploadID is non-empty. An empty identifier means finalize was attempted without a successful prepare, so the server would have no session to finish.

Source

Thrown at drivers/doubao_new/util.go:876

	values.Set("mount_point", "explorer")
	values.Set("doubao_storage", "imagex_other")
	values.Set("doubao_app_id", "497858")
	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")

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Check the prepare result immediately after it returns and abort the upload if UploadID is empty.
  2. Ensure error paths short-circuit (return on first error) so finish is never reached with partial state.
  3. Log the full prepare response once to confirm which field carries the upload_id.

Example fix

// before
prep, _ := d.prepareUpload(ctx, ...)
data, err := d.finishUpload(ctx, prep.UploadID, n, "explorer")

// after
prep, err := d.prepareUpload(ctx, ...)
if err != nil { return UploadFinishData{}, err }
if prep.UploadID == "" { return UploadFinishData{}, fmt.Errorf("[doubao_new] prepare returned empty upload_id") }
data, err := d.finishUpload(ctx, prep.UploadID, n, "explorer")
Defensive patterns

Strategy: validation

Validate before calling

prep, err := d.prepareUpload(ctx, ...)
if err != nil { return err }
if prep.UploadID == "" { return fmt.Errorf("prepare returned empty upload_id") }

Prevention

When it happens

Trigger: Calling finishUpload after prepare failed or returned an empty UploadID; state lost between prepare and finish (variable shadowing, early error swallowed); direct invocation with "".

Common situations: Error-handling paths that continue to finish even when prepare errored; API schema change emptying the UploadID field; refactors that rename the variable and pass the wrong one.

Related errors


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