chenhg5/cc-connect · error

upload init: %w

Error message

upload init: %w

What it means

After sending the aibot_upload_media_init frame, uploadWSMedia waits for the server's ack via writeAndWaitFrameWithTimeout with wsMediaAckTimeout. Any failure to write the frame or receive the ack within the timeout is wrapped as "upload init: %w", so the root cause (deadline exceeded, connection closed, write error) is preserved in the chain.

Source

Thrown at platform/wecom/websocket_outbound_media.go:68

		return "", fmt.Errorf("media too large: %d chunks exceeds maximum %d", totalChunks, wecomWSUploadMaxChunks)
	}

	sum := md5.Sum(data)
	initReqID := p.generateReqID("aibot_upload_media_init")
	initFrame := map[string]any{
		"cmd":     "aibot_upload_media_init",
		"headers": map[string]string{"req_id": initReqID},
		"body": map[string]any{
			"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")

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped root error: context.DeadlineExceeded means raise/investigate wsMediaAckTimeout; connection errors mean reconnect first.
  2. Retry the upload with backoff — a transient network hiccup or missed ack often succeeds on a second attempt.
  3. Verify the WebSocket connection is healthy before uploading (ping/pong or reconnect on stale connections).
  4. Check network path to WeCom (proxy, firewall, corporate egress) if failures are persistent.

Example fix

// before
mediaID, err := p.uploadWSMedia(ctx, "image", name, data)
if err != nil {
    return err
}

// after
mediaID, err := p.uploadWSMedia(ctx, "image", name, data)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        return fmt.Errorf("wecom: media upload init timed out; check WebSocket connectivity and wsMediaAckTimeout: %w", err)
    }
    return fmt.Errorf("wecom: media upload failed: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

if p.wsConn == nil || isStale(p.wsConn) {
    if err := p.reconnect(ctx); err != nil {
        return fmt.Errorf("wecom ws not connected: %w", err)
    }
}

Try / catch

mediaID, err := p.uploadWSMedia(ctx, mt, name, data)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) || isConnErr(err) {
        return retryWithBackoff(ctx, 3, func() (string, error) { return p.uploadWSMedia(ctx, mt, name, data) })
    }
    return "", err
}

Prevention

When it happens

Trigger: SendImage/SendFile when the WeCom WebSocket connection is slow, saturated, dead, or the server never acks aibot_upload_media_init within wsMediaAckTimeout; also when the frame write itself fails.

Common situations: Unstable or high-latency network to WeCom; WS connection dropped by a proxy/firewall mid-session; server overloaded or slow to ack; timeout too aggressive for large-scale deployments; stale connection that only fails on the next write.

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