{"record":{"id":"c0b66be9419f53c9","repo":"Tencent/WeKnora","slug":"decode-qqbot-response-w","errorCode":null,"errorMessage":"decode qqbot response: %w","messagePattern":"decode qqbot response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/im/qqbot/client.go","lineNumber":172,"sourceCode":"\t\t\treturn err\n\t\t}\n\t\treq.Header.Set(\"Authorization\", \"QQBot \"+token)\n\t}\n\n\tresp, err := c.httpClient.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\treturn fmt.Errorf(\"qqbot api %s %s failed: %s\", method, url, resp.Status)\n\t}\n\tif out == nil {\n\t\treturn nil\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(out); err != nil {\n\t\treturn fmt.Errorf(\"decode qqbot response: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (c *Client) AccessToken(ctx context.Context) (string, error) {\n\tc.mu.Lock()\n\tif c.accessToken != \"\" && time.Until(c.expiresAt) > time.Minute {\n\t\ttoken := c.accessToken\n\t\tc.mu.Unlock()\n\t\treturn token, nil\n\t}\n\tc.mu.Unlock()\n\n\tbody := map[string]string{\n\t\t\"appId\":        c.appID,\n\t\t\"clientSecret\": c.clientSecret,\n\t}\n\tvar result tokenResponse","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/im/qqbot/client.go#L154-L190","documentation":"The QQ bot API returned a 2xx status but the body could not be decoded into the expected response struct. doJSON wraps the json.Decoder error with this message so the root JSON parse cause is preserved.","triggerScenarios":"GatewayURL, sendText, or AccessToken receives a 2xx response whose body is HTML (proxy error page), truncated, empty, or whose shape changed.","commonSituations":"Corporate proxy/gateway returning HTML with 200; QQ API response schema drift; interrupted connection truncating the body; hitting a wrong endpoint that returns 200 with a different payload.","solutions":["Log the raw response body to see what was actually returned","Check for a proxy/CDN intercepting the request and bypass it","Upgrade the library if the QQ API response schema changed","Retry the request — transient truncation can produce this"],"exampleFix":"// before\nif err := json.NewDecoder(resp.Body).Decode(out); err != nil { return err } // opaque\n// after\nbody, _ := io.ReadAll(resp.Body)\nif err := json.Unmarshal(body, out); err != nil {\n    return fmt.Errorf(\"decode qqbot response: %w; body=%s\", err, body)\n}","handlingStrategy":"try-catch","validationCode":"// verify endpoint reachability and that it returns JSON before decoding\nresp, _ := http.Get(apiBase + \"/gateway\")\nct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") {\n    return fmt.Errorf(\"unexpected content-type %q (proxy error page?)\", ct)\n}","typeGuard":null,"tryCatchPattern":"if err := json.NewDecoder(resp.Body).Decode(out); err != nil {\n    body, _ := io.ReadAll(io.MultiReader(...)) // capture body for diagnostics\n    return fmt.Errorf(\"decode qqbot response: %w\", err)\n}","preventionTips":["Log raw response bodies on decode failure","Check Content-Type before decoding","Bypass intercepting proxies for API calls","Pin/upgrade client when QQ changes schemas"],"tags":["json","decoding","qqbot","api"],"backgroundTag":"json-decode-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}