chenhg5/cc-connect · error

upload finish: empty media_id

Error message

upload finish: empty media_id

What it means

The finish ack decoded successfully but contained an empty media_id, meaning WeCom accepted the finish command yet did not return the identifier needed to send the media message. This is treated as a failed upload because sendWSMediaMessage cannot proceed without a media_id.

Source

Thrown at platform/wecom/websocket_outbound_media.go:120

	finishFrame := map[string]any{
		"cmd":     "aibot_upload_media_finish",
		"headers": map[string]string{"req_id": finishReqID},
		"body": map[string]any{
			"upload_id": initBody.UploadID,
		},
	}
	finishResp, err := p.writeAndWaitFrameWithTimeout(ctx, finishFrame, finishReqID, wsMediaAckTimeout)
	if err != nil {
		return "", fmt.Errorf("upload finish: %w", err)
	}
	var finishBody struct {
		MediaID string `json:"media_id"`
	}
	if err := json.Unmarshal(finishResp.Body, &finishBody); err != nil {
		return "", fmt.Errorf("decode upload finish response: %w", err)
	}
	if finishBody.MediaID == "" {
		return "", fmt.Errorf("upload finish: empty media_id")
	}
	return finishBody.MediaID, nil
}

func (p *WSPlatform) sendWSMediaMessage(ctx context.Context, chatID, mediaType, mediaID string) error {
	reqID := p.generateReqID("aibot_send_msg")
	frame := map[string]any{
		"cmd":     "aibot_send_msg",
		"headers": map[string]string{"req_id": reqID},
		"body": map[string]any{
			"chatid":  chatID,
			"msgtype": mediaType,
			mediaType: map[string]string{
				"media_id": mediaID,
			},
		},
	}
	return p.writeAndWaitAckStrict(ctx, frame, reqID, wsMediaAckTimeout)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Dump the raw finish response body to check if media_id moved to a nested field and update the struct tags
  2. Retry the upload once — a transient race where finalization is not complete can yield an empty id
  3. Verify all chunk acks succeeded before finish; a rejected chunk can still produce an empty media_id
  4. Check WeCom AI Bot API changelog for response schema changes

Example fix

// before
var finishBody struct {
	MediaID string `json:"media_id"`
}
// after
var finishBody struct {
	MediaID string `json:"media_id"`
	Data    struct {
		MediaID string `json:"media_id"`
	} `json:"data"`
}
func (b finishBodyType) id() string { ... } // pick b.MediaID or b.Data.MediaID
Defensive patterns

Strategy: retry

Try / catch

if err := p.SendFile(ctx, rc, file); err != nil && strings.Contains(err.Error(), "empty media_id") {
	// one retry; if it persists, log the raw finish body for schema drift
}

Prevention

When it happens

Trigger: Called from SendImage/SendFile via uploadWSMedia when finishBody.MediaID == "" after successful JSON decoding of the finish ack.

Common situations: WeCom server-side processing not complete when finish acked (async finalization); the upload_id referenced no valid chunks; API schema change moved media_id into a nested field so it decodes to empty.

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/aaacfb913efc4b93. Report an issue: GitHub.