{"record":{"id":"5ed8d67162732220","repo":"chenhg5/cc-connect","slug":"upload-init-w","errorCode":null,"errorMessage":"upload init: %w","messagePattern":"upload init: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wecom/websocket_outbound_media.go","lineNumber":68,"sourceCode":"\t\treturn \"\", fmt.Errorf(\"media too large: %d chunks exceeds maximum %d\", totalChunks, wecomWSUploadMaxChunks)\n\t}\n\n\tsum := md5.Sum(data)\n\tinitReqID := p.generateReqID(\"aibot_upload_media_init\")\n\tinitFrame := map[string]any{\n\t\t\"cmd\":     \"aibot_upload_media_init\",\n\t\t\"headers\": map[string]string{\"req_id\": initReqID},\n\t\t\"body\": map[string]any{\n\t\t\t\"type\":         mediaType,\n\t\t\t\"filename\":     filename,\n\t\t\t\"total_size\":   len(data),\n\t\t\t\"total_chunks\": totalChunks,\n\t\t\t\"md5\":          hex.EncodeToString(sum[:]),\n\t\t},\n\t}\n\tinitResp, err := p.writeAndWaitFrameWithTimeout(ctx, initFrame, initReqID, wsMediaAckTimeout)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"upload init: %w\", err)\n\t}\n\tvar initBody struct {\n\t\tUploadID string `json:\"upload_id\"`\n\t}\n\tif err := json.Unmarshal(initResp.Body, &initBody); err != nil {\n\t\treturn \"\", fmt.Errorf(\"decode upload init response: %w\", err)\n\t}\n\tif initBody.UploadID == \"\" {\n\t\treturn \"\", fmt.Errorf(\"upload init: empty upload_id\")\n\t}\n\n\tfor i := 0; i < totalChunks; i++ {\n\t\tstart := i * wecomWSUploadChunkSize\n\t\tend := start + wecomWSUploadChunkSize\n\t\tif end > len(data) {\n\t\t\tend = len(data)\n\t\t}\n\t\treqID := p.generateReqID(\"aibot_upload_media_chunk\")","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wecom/websocket_outbound_media.go#L50-L86","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the wrapped root error: context.DeadlineExceeded means raise/investigate wsMediaAckTimeout; connection errors mean reconnect first.","Retry the upload with backoff — a transient network hiccup or missed ack often succeeds on a second attempt.","Verify the WebSocket connection is healthy before uploading (ping/pong or reconnect on stale connections).","Check network path to WeCom (proxy, firewall, corporate egress) if failures are persistent."],"exampleFix":"// before\nmediaID, err := p.uploadWSMedia(ctx, \"image\", name, data)\nif err != nil {\n    return err\n}\n\n// after\nmediaID, err := p.uploadWSMedia(ctx, \"image\", name, data)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        return fmt.Errorf(\"wecom: media upload init timed out; check WebSocket connectivity and wsMediaAckTimeout: %w\", err)\n    }\n    return fmt.Errorf(\"wecom: media upload failed: %w\", err)\n}","handlingStrategy":"retry","validationCode":"if p.wsConn == nil || isStale(p.wsConn) {\n    if err := p.reconnect(ctx); err != nil {\n        return fmt.Errorf(\"wecom ws not connected: %w\", err)\n    }\n}","typeGuard":null,"tryCatchPattern":"mediaID, err := p.uploadWSMedia(ctx, mt, name, data)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) || isConnErr(err) {\n        return retryWithBackoff(ctx, 3, func() (string, error) { return p.uploadWSMedia(ctx, mt, name, data) })\n    }\n    return \"\", err\n}","preventionTips":["Keep the WebSocket alive with periodic ping/pong and reconnect on missed pongs.","Size wsMediaAckTimeout generously for high-latency networks (e.g. >=30s for media init).","Wrap uploads in bounded exponential-backoff retry for transient failures.","Monitor WS disconnect/proxy logs; corporate proxies often kill idle sockets."],"tags":["wecom","websocket","timeout","media-upload","network"],"backgroundTag":"request-timeout","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}