{"record":{"id":"b358009c65bf2059","repo":"chenhg5/cc-connect","slug":"upload-chunk-d-w","errorCode":null,"errorMessage":"upload chunk %d: %w","messagePattern":"upload chunk (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wecom/websocket_outbound_media.go","lineNumber":97,"sourceCode":"\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\")\n\t\tchunkFrame := map[string]any{\n\t\t\t\"cmd\":     \"aibot_upload_media_chunk\",\n\t\t\t\"headers\": map[string]string{\"req_id\": reqID},\n\t\t\t\"body\": map[string]any{\n\t\t\t\t\"upload_id\":   initBody.UploadID,\n\t\t\t\t\"chunk_index\": i,\n\t\t\t\t\"base64_data\": base64.StdEncoding.EncodeToString(data[start:end]),\n\t\t\t},\n\t\t}\n\t\tif _, err := p.writeAndWaitFrameWithTimeout(ctx, chunkFrame, reqID, wsMediaAckTimeout); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"upload chunk %d: %w\", i, err)\n\t\t}\n\t}\n\n\tfinishReqID := p.generateReqID(\"aibot_upload_media_finish\")\n\tfinishFrame := map[string]any{\n\t\t\"cmd\":     \"aibot_upload_media_finish\",\n\t\t\"headers\": map[string]string{\"req_id\": finishReqID},\n\t\t\"body\": map[string]any{\n\t\t\t\"upload_id\": initBody.UploadID,\n\t\t},\n\t}\n\tfinishResp, err := p.writeAndWaitFrameWithTimeout(ctx, finishFrame, finishReqID, wsMediaAckTimeout)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"upload finish: %w\", err)\n\t}\n\tvar finishBody struct {\n\t\tMediaID string `json:\"media_id\"`\n\t}","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wecom/websocket_outbound_media.go#L79-L115","documentation":"uploadWSMedia sends the file data to WeCom's AI Bot WebSocket API in base64-encoded chunks via 'aibot_upload_media_chunk' frames and waits for an ack for each chunk. This error wraps the failure of one of those per-chunk write/ack round trips (timeout, disconnected socket, or protocol error). The wrapped error identifies the underlying cause; the %d tells which chunk index failed.","triggerScenarios":"Called from SendImage or SendFile via uploadWSMedia when writeAndWaitFrameWithTimeout for chunk frame i returns an error: WS connection closed, ack not received within wsMediaAckTimeout, or the frame write failed.","commonSituations":"WebSocket dropped mid-upload (network blip, WeCom server restart); large files whose upload takes longer than the per-chunk ack timeout; token/session expiry invalidating the WS session; concurrent requests over the same WS connection desyncing request IDs.","solutions":["Check the wrapped cause: if it is a timeout, increase wsMediaAckTimeout or reduce chunk size for slow links","Verify the WebSocket connection is alive before uploading (reconnect if needed) and retry the whole upload","Confirm the WeCom AI Bot session/credentials are still valid — an expired session often fails mid-upload","Log reqID and chunk index to correlate with WeCom server-side logs"],"exampleFix":"// before\nif _, err := p.writeAndWaitFrameWithTimeout(ctx, chunkFrame, reqID, wsMediaAckTimeout); err != nil {\n\treturn \"\", fmt.Errorf(\"upload chunk %d: %w\", i, err)\n}\n// after\nif _, err := p.writeAndWaitFrameWithTimeout(ctx, chunkFrame, reqID, wsMediaAckTimeout); err != nil {\n\tif p.conn == nil { // stale connection: reconnect once, then retry chunk\n\t\tif rerr := p.reconnect(ctx); rerr != nil {\n\t\t\treturn \"\", fmt.Errorf(\"upload chunk %d: reconnect: %w\", i, rerr)\n\t\t}\n\t\treturn p.uploadWSMedia(ctx, mediaType, fileName, data)\n\t}\n\treturn \"\", fmt.Errorf(\"upload chunk %d: %w\", i, err)\n}","handlingStrategy":"try-catch","validationCode":"if len(file.Data) == 0 || !p.IsConnected() {\n\treturn fmt.Errorf(\"wecom-ws: not connected or empty data, skipping upload\")\n}","typeGuard":null,"tryCatchPattern":"if err := p.SendFile(ctx, rc, file); err != nil {\n\tvar uerr *uploadChunkError\n\tif errors.As(err, &uerr) && isTimeout(uerr.Unwrap()) {\n\t\t// reconnect and retry once\n\t}\n\tlog.Warn(\"wecom file upload failed\", \"err\", err)\n}","preventionTips":["Verify WS connectivity before starting uploads","Keep chunk sizes modest so each ack lands well within wsMediaAckTimeout","Retry full uploads idempotently with fresh upload_id","Monitor WS reconnect frequency as an early warning"],"tags":["websocket","file-upload","network","timeout"],"backgroundTag":"request-timeout","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}