chenhg5/cc-connect · error

upload init: empty upload_id

Error message

upload init: empty upload_id

What it means

The aibot_upload_media_init ack decoded successfully as JSON, but the upload_id field is empty. The upload_id is required to reference the subsequent chunk uploads, so without it the chunked upload cannot proceed and uploadWSMedia aborts with this explicit error rather than sending chunks the server can't reassemble.

Source

Thrown at platform/wecom/websocket_outbound_media.go:77

			"type":         mediaType,
			"filename":     filename,
			"total_size":   len(data),
			"total_chunks": totalChunks,
			"md5":          hex.EncodeToString(sum[:]),
		},
	}
	initResp, err := p.writeAndWaitFrameWithTimeout(ctx, initFrame, initReqID, wsMediaAckTimeout)
	if err != nil {
		return "", fmt.Errorf("upload init: %w", err)
	}
	var initBody struct {
		UploadID string `json:"upload_id"`
	}
	if err := json.Unmarshal(initResp.Body, &initBody); err != nil {
		return "", fmt.Errorf("decode upload init response: %w", err)
	}
	if initBody.UploadID == "" {
		return "", fmt.Errorf("upload init: empty upload_id")
	}

	for i := 0; i < totalChunks; i++ {
		start := i * wecomWSUploadChunkSize
		end := start + wecomWSUploadChunkSize
		if end > len(data) {
			end = len(data)
		}
		reqID := p.generateReqID("aibot_upload_media_chunk")
		chunkFrame := map[string]any{
			"cmd":     "aibot_upload_media_chunk",
			"headers": map[string]string{"req_id": reqID},
			"body": map[string]any{
				"upload_id":   initBody.UploadID,
				"chunk_index": i,
				"base64_data": base64.StdEncoding.EncodeToString(data[start:end]),
			},
		}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Log the full ack body to check for an embedded error code/message the server returned instead of an upload_id.
  2. Verify bot credentials/token are valid and not expired — rejections often arrive as JSON without upload_id.
  3. Check for a WeCom API schema change and update the initBody struct (e.g. nested field or renamed key).
  4. Confirm the mediaType passed by SendImage/SendFile is one the server accepts for this upload command.

Example fix

// before
if initBody.UploadID == "" {
    return "", fmt.Errorf("upload init: empty upload_id")
}

// after
if initBody.UploadID == "" {
    if initBody.ErrCode != 0 {
        return "", fmt.Errorf("upload init rejected: errcode=%d errmsg=%s", initBody.ErrCode, initBody.ErrMsg)
    }
    return "", fmt.Errorf("upload init: empty upload_id (body=%q)", string(initResp.Body))
}
Defensive patterns

Strategy: type-guard

Validate before calling

type initAck struct {
    UploadID string `json:"upload_id"`
    ErrCode  int    `json:"errcode"`
    ErrMsg   string `json:"errmsg"`
}
func ackHasUploadID(body []byte) bool {
    var a initAck
    if json.Unmarshal(body, &a) != nil {
        return false
    }
    return a.ErrCode == 0 && a.UploadID != ""
}

Type guard

func uploadIDFromAck(body []byte) (string, error) {
    var a initAck
    if err := json.Unmarshal(body, &a); err != nil {
        return "", err
    }
    if a.ErrCode != 0 {
        return "", fmt.Errorf("server error %d: %s", a.ErrCode, a.ErrMsg)
    }
    if a.UploadID == "" {
        return "", fmt.Errorf("ack missing upload_id: %s", string(body))
    }
    return a.UploadID, nil
}

Try / catch

uploadID, err := uploadIDFromAck(initResp.Body)
if err != nil {
    slog.Warn("wecom upload init: no upload_id", "body", string(initResp.Body), "err", err)
    return "", fmt.Errorf("upload init: %w", err)
}

Prevention

When it happens

Trigger: SendImage/SendFile when the WeCom server acks aibot_upload_media_init with a body lacking upload_id (e.g. an error ack like {"errcode":...} that still parses as JSON, or a schema where the field was renamed) so initBody.UploadID ends up "".

Common situations: WeCom API version change renaming or nesting the upload_id field; server-side rejection (auth expired, quota, invalid media type) returned in a JSON body without upload_id; bot credentials lacking the upload permission; sending a media_type the server session doesn't accept.

Understand the failure class

Background: "empty response", "returned no data", "empty embeddings": what HTTP 200-with-empty-body errors mean across libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/32adadf19740e40b. Report an issue: GitHub.