{"record":{"id":"6cd28887705a7ae9","repo":"chenhg5/cc-connect","slug":"qq-http-s-read-body-w","errorCode":null,"errorMessage":"qq: HTTP %s read body: %w","messagePattern":"qq: HTTP (.+?) read body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qq/qq.go","lineNumber":616,"sourceCode":"\turl := p.httpURL + \"/\" + action\n\treq, err := http.NewRequest(\"POST\", url, bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tif p.token != \"\" {\n\t\treq.Header.Set(\"Authorization\", \"Bearer \"+p.token)\n\t}\n\tclient := &http.Client{Timeout: 120 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"qq: HTTP %s failed: %w\", action, err)\n\t}\n\tdefer resp.Body.Close()\n\n\traw, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"qq: HTTP %s read body: %w\", action, err)\n\t}\n\n\tvar apiResp struct {\n\t\tStatus  string          `json:\"status\"`\n\t\tRetCode int             `json:\"retcode\"`\n\t\tData    json.RawMessage `json:\"data\"`\n\t\tMessage string          `json:\"message\"`\n\t}\n\tif json.Unmarshal(raw, &apiResp) != nil {\n\t\treturn nil, fmt.Errorf(\"qq: HTTP %s invalid response\", action)\n\t}\n\tif apiResp.RetCode != 0 {\n\t\treturn nil, fmt.Errorf(\"qq: HTTP %s failed (retcode=%d, msg=%s)\", action, apiResp.RetCode, apiResp.Message)\n\t}\n\tvar result map[string]any\n\t_ = json.Unmarshal(apiResp.Data, &result)\n\treturn result, nil\n}","sourceCodeStart":598,"sourceCodeEnd":634,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qq/qq.go#L598-L634","documentation":"callHTTPAPI returns this when io.ReadAll fails while reading the response body of a QQ HTTP API call. The HTTP response was received (status line/headers), but the body could not be fully read — typically a mid-transfer connection reset or timeout. The action name is included to identify which API call failed.","triggerScenarios":"client.Do succeeded but io.ReadAll(resp.Body) returned an error during parseMessage's callHTTPAPI call: connection dropped mid-body, keep-alive reuse of a stale connection, or server closing the socket prematurely.","commonSituations":"go-cqhttp/OneBot server restarting during a request; reverse proxy (nginx) closing idle keep-alive connections; network interruption between cc-connect and the QQ server.","solutions":["Retry the failed action; this is usually a transient transport error.","Check QQ server logs for premature connection closes or crashes.","Disable or tune keep-alive/proxy idle timeouts if errors cluster after idle periods.","Check network stability between cc-connect and the QQ HTTP endpoint."],"exampleFix":"// before\nraw, err := io.ReadAll(resp.Body)\nif err != nil {\n    return nil, fmt.Errorf(\"qq: HTTP %s read body: %w\", action, err)\n}\n// after (bounded read with a size cap)\nraw, err := io.ReadAll(io.LimitReader(resp.Body, 8<<20))\nif err != nil {\n    return nil, fmt.Errorf(\"qq: HTTP %s read body: %w\", action, err)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil {\n    if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, context.Canceled) {\n        // transient: retry the action once\n    }\n    return fmt.Errorf(\"qq api body read failed: %w\", err)\n}","preventionTips":["Tune proxy/load-balancer idle timeouts to exceed the 120s client timeout.","Retry idempotent read actions on body-read errors.","Watch for server restarts coinciding with the error in QQ server logs."],"tags":["network","http-client","qq","io"],"backgroundTag":"network-request-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"}