chenhg5/cc-connect · error

upload finish: %w

Error message

upload finish: %w

What it means

After all chunks are sent, uploadWSMedia sends an 'aibot_upload_media_finish' frame and waits for the ack that carries the final media_id. This error wraps a failure of that finish round trip (timeout, closed socket, or server rejection of the finish command). Without a successful finish the uploaded chunks are discarded server-side and no media_id is produced.

Source

Thrown at platform/wecom/websocket_outbound_media.go:111

				"base64_data": base64.StdEncoding.EncodeToString(data[start:end]),
			},
		}
		if _, err := p.writeAndWaitFrameWithTimeout(ctx, chunkFrame, reqID, wsMediaAckTimeout); err != nil {
			return "", fmt.Errorf("upload chunk %d: %w", i, err)
		}
	}

	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},

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped cause; on timeout retry the entire upload (init → chunks → finish) with a fresh upload_id
  2. Verify the WS connection is still open and reconnect before retrying
  3. Increase wsMediaAckTimeout if finishes regularly time out on large media
  4. Check WeCom AI Bot API status/permissions if finish is consistently rejected
Defensive patterns

Strategy: retry

Validate before calling

if !p.IsConnected() {
	if err := p.reconnect(ctx); err != nil {
		return err
	}
}

Try / catch

mediaID, err := p.SendFile(ctx, rc, file)
if err != nil && strings.Contains(err.Error(), "upload finish") {
	// whole upload is discarded; retry once end-to-end
	return p.SendFile(ctx, rc, file)
}

Prevention

When it happens

Trigger: Called from SendImage/SendFile via uploadWSMedia when writeAndWaitFrameWithTimeout for the aibot_upload_media_finish frame returns an error.

Common situations: WeCom rejects the finish because a chunk failed validation server-side; the WS ack timed out (wsMediaAckTimeout) under load; connection dropped between last chunk and finish; upload_id no longer valid due to server-side session expiry.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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