{"record":{"id":"4b1344bef7f8f88a","repo":"chenhg5/cc-connect","slug":"decode-upload-init-response-w","errorCode":null,"errorMessage":"decode upload init response: %w","messagePattern":"decode upload init response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wecom/websocket_outbound_media.go","lineNumber":74,"sourceCode":"\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\")\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,","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wecom/websocket_outbound_media.go#L56-L92","documentation":"uploadWSMedia received the aibot_upload_media_init ack but its Body could not be parsed as JSON into the expected {upload_id: string} shape. json.Unmarshal fails on invalid JSON or on a shape mismatch (e.g. upload_id is not a string), and the parse error is wrapped as \"decode upload init response: %w\".","triggerScenarios":"SendImage/SendFile when the WeCom server returns malformed JSON, an HTML/error page, a binary frame, or a JSON body whose upload_id field has a non-string type (null, number, object) instead of the expected string.","commonSituations":"WeCom API version change altering the response schema; server returning an error payload (e.g. {\"errcode\":...,\"errmsg\":...} with different field types) that doesn't match the struct; gateway/proxy injecting non-JSON content; truncated frames.","solutions":["Log the raw initResp.Body (hex/string) at debug level to see exactly what the server returned.","Check for a WeCom API/schema change and update the initBody struct tags to match the current response format.","Handle structured server error responses (errcode/errmsg) explicitly before trying to decode upload_id.","Confirm the frame you're decoding is the ack to your init request (request-ID routing) and not an unrelated frame."],"exampleFix":"// before\nvar initBody struct {\n    UploadID string `json:\"upload_id\"`\n}\nif err := json.Unmarshal(initResp.Body, &initBody); err != nil {\n    return \"\", fmt.Errorf(\"decode upload init response: %w\", err)\n}\n\n// after\nvar initBody struct {\n    UploadID string `json:\"upload_id\"`\n    ErrCode  int    `json:\"errcode\"`\n    ErrMsg   string `json:\"errmsg\"`\n}\nif err := json.Unmarshal(initResp.Body, &initBody); err != nil {\n    return \"\", fmt.Errorf(\"decode upload init response (body=%q): %w\", string(initResp.Body), err)\n}\nif initBody.ErrCode != 0 {\n    return \"\", fmt.Errorf(\"upload init rejected: errcode=%d errmsg=%s\", initBody.ErrCode, initBody.ErrMsg)\n}","handlingStrategy":"type-guard","validationCode":"func looksLikeJSON(b []byte) bool {\n    t := bytes.TrimSpace(b)\n    return len(t) > 0 && (t[0] == '{' || t[0] == '[')\n}\n// check initResp.Body before unmarshalling; log raw body if not JSON","typeGuard":"func isInitAck(body []byte) (uploadID string, ok bool) {\n    var v struct {\n        UploadID any `json:\"upload_id\"`\n    }\n    if json.Unmarshal(body, &v) != nil {\n        return \"\", false\n    }\n    s, isStr := v.UploadID.(string)\n    return s, isStr && s != \"\"\n}","tryCatchPattern":"if err := json.Unmarshal(initResp.Body, &initBody); err != nil {\n    slog.Error(\"wecom upload init: unparseable ack\", \"body\", string(initResp.Body), \"err\", err)\n    return \"\", fmt.Errorf(\"decode upload init response: %w\", err)\n}","preventionTips":["Always log raw response bodies at debug level for upload handshakes.","Model error responses (errcode/errmsg) in your structs so rejections parse cleanly.","Pin and test against specific WeCom API versions; watch changelogs for schema changes.","Validate frame routing (request-ID match) before decoding bodies."],"tags":["wecom","websocket","json","media-upload","response-parsing"],"backgroundTag":"json-unmarshal-failed","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"}