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
- Dump the raw finish response body to check if media_id moved to a nested field and update the struct tags
- Retry the upload once — a transient race where finalization is not complete can yield an empty id
- Verify all chunk acks succeeded before finish; a rejected chunk can still produce an empty media_id
- 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
- Verify every chunk ack before sending finish
- Log the raw finish ack body to catch schema drift early
- Retry once for transient empty responses
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
- qqbot: upload rich media: empty file_info
- upload init: empty upload_id
- upload chunk %d: %w
- upload finish: %w
- decode upload finish response: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/aaacfb913efc4b93.
Report an issue: GitHub.