{"record":{"id":"3a126e459645f025","repo":"chenhg5/cc-connect","slug":"qqbot-decode-response-w","errorCode":null,"errorMessage":"qqbot: decode response: %w","messagePattern":"qqbot: decode response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qqbot/qqbot.go","lineNumber":362,"sourceCode":"\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"qqbot: build retry request: %w\", err)\n\t\t}\n\t\treq2.Header.Set(\"Authorization\", \"QQBot \"+token)\n\t\treq2.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tresp2, err := core.HTTPClient.Do(req2)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"qqbot: api retry failed: %w\", err)\n\t\t}\n\t\tdefer resp2.Body.Close()\n\n\t\tif resp2.StatusCode >= 300 {\n\t\t\traw, _ := io.ReadAll(resp2.Body)\n\t\t\treturn fmt.Errorf(\"qqbot: api %s %s returned %d (after retry): %s\", method, url, resp2.StatusCode, raw)\n\t\t}\n\t\tif result != nil {\n\t\t\tif err := json.NewDecoder(resp2.Body).Decode(result); err != nil {\n\t\t\t\treturn fmt.Errorf(\"qqbot: decode response: %w\", err)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tif resp.StatusCode >= 300 {\n\t\traw, _ := io.ReadAll(resp.Body)\n\t\treturn fmt.Errorf(\"qqbot: api %s %s returned %d: %s\", method, url, resp.StatusCode, raw)\n\t}\n\tif result != nil {\n\t\tif err := json.NewDecoder(resp.Body).Decode(result); err != nil {\n\t\t\treturn fmt.Errorf(\"qqbot: decode response: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\nvar _ core.ImageSender = (*Platform)(nil)","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qqbot/qqbot.go#L344-L380","documentation":"On the 401-retry path, when the retried response status is 2xx but the JSON body cannot be decoded into the caller's result struct, this wrapped error is returned. It indicates the QQ API returned a successful status with an unexpected or malformed payload — e.g. an HTML error page behind a proxy or an undocumented response shape.","triggerScenarios":"resp2 is 2xx and json.Decoder.Decode(result) fails: truncated body, non-JSON content (proxy/gateway interference), or a field in the result struct whose type does not match the actual JSON (e.g. string vs number).","commonSituations":"Corporate proxy or captive portal returning HTML with 200; QQ API schema change; uploadRichMedia's {file_info} struct receiving a different shape; gzip/encoding issues from a misconfigured middlebox.","solutions":["Log the raw response body at the failure point to see what was actually returned.","Check for a proxy/gateway rewriting responses (HTML in body = middlebox issue).","Verify the result struct's JSON tags against the current QQ Bot API docs.","Update to the latest cc-connect version in case the API schema changed.","If the body is empty, check whether the endpoint now returns 204 with no body."],"exampleFix":"// before\nvar result struct{ FileInfo string `json:\"file_info\"` }\n// after (tolerate alternate field naming confirmed from API docs)\nvar result struct{ FileInfo string `json:\"file_info\"` }\n// (fix usually means updating the struct tag or handling a 204/no-body response, not the caller)","handlingStrategy":"type-guard","validationCode":"// Sanity-check the raw body before decoding in a wrapper:\nraw, _ := io.ReadAll(resp.Body)\nif !json.Valid(raw) || len(raw) == 0 {\n    return fmt.Errorf(\"qqbot: non-JSON or empty response from proxy/api\")\n}","typeGuard":null,"tryCatchPattern":"if err := send(); err != nil {\n    if strings.Contains(err.Error(), \"decode response\") {\n        slog.Warn(\"qqbot returned unexpected 2xx body; check proxy/interference\", \"err\", err)\n    }\n}","preventionTips":["Ensure no proxy/gateway rewrites HTTPS responses (HTML with 200).","Keep cc-connect updated against QQ API schema changes.","Log raw bodies on decode failure to diagnose quickly.","Verify expected endpoints actually return bodies (some may legitimately be empty)."],"tags":["qqbot","json","http"],"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-14T00:17:10.932Z"}