chenhg5/cc-connect · error

decode upload finish response: %w

Error message

decode upload finish response: %w

What it means

The aibot_upload_media_finish ack arrived but its body could not be parsed as JSON containing a 'media_id' field. This indicates the server returned an unexpected body — often an error payload or a protocol version mismatch rather than the expected {"media_id":"..."} shape.

Source

Thrown at platform/wecom/websocket_outbound_media.go:117

	}

	finishReqID := p.generateReqID("aibot_upload_media_finish")
	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,
			},

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Log finishResp.Body verbatim to see what the server actually returned
  2. Check for an embedded error code/message in the body and surface it instead of the raw unmarshal error
  3. Update the finishBody struct to match the current WeCom AI Bot API response schema
  4. Confirm you are on a supported WeCom AI Bot protocol version

Example fix

// before
if err := json.Unmarshal(finishResp.Body, &finishBody); err != nil {
	return "", fmt.Errorf("decode upload finish response: %w", err)
}
// after
if err := json.Unmarshal(finishResp.Body, &finishBody); err != nil {
	return "", fmt.Errorf("decode upload finish response %s: %w", string(finishResp.Body), err)
}
Defensive patterns

Strategy: try-catch

Type guard

func isValidFinishBody(b []byte) bool {
	var probe struct {
		MediaID string `json:"media_id"`
	}
	return json.Unmarshal(b, &probe) == nil
}

Try / catch

mediaID, err := p.SendFile(ctx, rc, file)
if err != nil && strings.Contains(err.Error(), "decode upload finish response") {
	log.Error("unexpected finish ack", "err", err) // inspect server body in logs
}

Prevention

When it happens

Trigger: Called from SendImage/SendFile via uploadWSMedia when json.Unmarshal(finishResp.Body, &finishBody) fails after a successful finish ack.

Common situations: WeCom API version changed the finish response schema; the server returned an error JSON (different fields) with a 200-style ack; a gateway/proxy substituted an HTML or text error body.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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