{"record":{"id":"2e62473baedb111a","repo":"chenhg5/cc-connect","slug":"weixin-getuploadurl-empty-upload-param-and-uploa","errorCode":null,"errorMessage":"weixin: getUploadUrl: empty upload_param and upload_full_url in %s","messagePattern":"weixin: getUploadUrl: empty upload_param and upload_full_url in (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/weixin/client.go","lineNumber":209,"sourceCode":"\nfunc (c *apiClient) getUploadURL(ctx context.Context, req getUploadURLRequest) (*getUploadURLResponse, error) {\n\treq.BaseInfo = baseInfo{ChannelVersion: channelVersion}\n\tpayload, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\traw, err := c.post(ctx, \"ilink/bot/getuploadurl\", payload, 0, \"getUploadUrl\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar out getUploadURLResponse\n\tif err := json.Unmarshal(raw, &out); err != nil {\n\t\treturn nil, fmt.Errorf(\"weixin: getUploadUrl json: %w\", err)\n\t}\n\t// 兼容微信 iLink API 变更：新版返回 upload_full_url 而非 upload_param\n\t// upload_full_url 是完整的 CDN 上传地址，可独立作为成功路径\n\tif strings.TrimSpace(out.UploadParam) == \"\" && strings.TrimSpace(out.UploadFullURL) == \"\" {\n\t\treturn nil, fmt.Errorf(\"weixin: getUploadUrl: empty upload_param and upload_full_url in %s\", truncateForLog(raw, 512))\n\t}\n\treturn &out, nil\n}\n\nfunc (c *apiClient) getConfig(ctx context.Context, userID, contextToken string) (*getConfigResp, error) {\n\treq := getConfigReq{\n\t\tUserID:       userID,\n\t\tContextToken: contextToken,\n\t\tBaseInfo:     baseInfo{ChannelVersion: channelVersion},\n\t}\n\tpayload, err := json.Marshal(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\traw, err := c.post(ctx, \"ilink/bot/getconfig\", payload, 0, \"getConfig\")\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/weixin/client.go#L191-L227","documentation":"After decoding getuploadurl's response, the client validates it actually contains an upload address. WeChat's iLink API changed from returning upload_param to upload_full_url; if BOTH are empty, no usable CDN upload target exists and the client refuses to continue with a body snippet for debugging.","triggerScenarios":"The API response decodes fine but lacks both UploadParam and UploadFullURL — server-side rejection embedded in a 200 body, degraded API response, or yet another schema change introducing a new field name.","commonSituations":"WeChat iLink API protocol drift (the exact scenario this guard was added for); expired ticket yielding an empty success body; regional API versions with different field names.","solutions":["Log/inspect the returned body snippet in the error to find the actual field names","Update getUploadURLResponse to parse the new upload-address field and treat it as valid","Re-authenticate/refresh the ticket if the API returns empty addresses for stale sessions","Retry once before surfacing the error, in case of transient degradation"],"exampleFix":"// before\nif strings.TrimSpace(out.UploadParam) == \"\" && strings.TrimSpace(out.UploadFullURL) == \"\" {\n\treturn nil, fmt.Errorf(\"weixin: getUploadUrl: empty upload_param and upload_full_url in %s\", ...)\n}\n// after: accept a newly introduced field too\nif strings.TrimSpace(out.UploadParam) == \"\" && strings.TrimSpace(out.UploadFullURL) == \"\" && strings.TrimSpace(out.UploadURL) == \"\" {\n\treturn nil, fmt.Errorf(\"weixin: getUploadUrl: no upload address in %s\", truncateForLog(raw, 512))\n}","handlingStrategy":"fallback","validationCode":"if strings.TrimSpace(resp.UploadParam) == \"\" && strings.TrimSpace(resp.UploadFullURL) == \"\" { /* treat as failure before calling CDN */ }","typeGuard":"func hasUploadAddress(r *getUploadURLResponse) bool {\n\treturn r != nil && (strings.TrimSpace(r.UploadParam) != \"\" || strings.TrimSpace(r.UploadFullURL) != \"\")\n}","tryCatchPattern":"addr, err := getUploadURL(ctx, req)\nif err != nil || !hasUploadAddress(addr) {\n\tslog.Warn(\"weixin: no upload address; retrying once\", \"err\", err)\n\taddr, err = getUploadURL(ctx, req)\n\tif err != nil { return err }\n}","preventionTips":["Track WeChat iLink API changelog for new upload-address fields","Refresh tickets when empty addresses recur","Retain the raw-body snippet in errors for fast diagnosis"],"tags":["weixin","upload","api-change","validation"],"backgroundTag":"unexpected-api-response-shape","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"}