{"record":{"id":"723790de73985fc0","repo":"chenhg5/cc-connect","slug":"read-body-failed","errorCode":null,"errorMessage":"read body failed","messagePattern":"read body failed","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"platform/feishu/feishu.go","lineNumber":730,"sourceCode":"\tp.cancel = cancel\n\tp.mu.Unlock()\n\n\tgo func() {\n\t\tslog.Info(p.tag()+\": webhook server listening\", \"port\", p.port, \"path\", p.callbackPath)\n\t\tif err := p.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n\t\t\tslog.Error(p.tag()+\": webhook server error\", \"error\", err)\n\t\t}\n\t}()\n\n\treturn nil\n}\n\n// webhookHandler handles HTTP webhook requests from Lark international version\nfunc (p *Platform) webhookHandler(w http.ResponseWriter, r *http.Request) {\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\tslog.Error(p.tag()+\": read webhook body failed\", \"error\", err)\n\t\thttp.Error(w, \"read body failed\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Build EventReq from HTTP request\n\treq := &larkevent.EventReq{\n\t\tHeader:     r.Header,\n\t\tBody:       body,\n\t\tRequestURI: r.RequestURI,\n\t}\n\n\t// Use the SDK's event dispatcher to handle the request\n\tresp := p.eventHandler.Handle(r.Context(), req)\n\n\t// Write response\n\tfor k, v := range resp.Header {\n\t\tw.Header()[k] = v\n\t}\n\tw.WriteHeader(resp.StatusCode)","sourceCodeStart":712,"sourceCodeEnd":748,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/feishu/feishu.go#L712-L748","documentation":"In the Lark-international webhook handler, reading the raw request body with io.ReadAll failed; the handler logs the error and responds 400 \"read body failed\". This happens before signature verification or event parsing, so nothing from the event was processed.","triggerScenarios":"Client aborting mid-upload, chunked request body stream erroring, Content-Length larger than allowed and connection cut, or transport-level failures between proxy and handler.","commonSituations":"Mobile/unstable networks dropping the POST; misconfigured reverse proxy with a small client_body_timeout; sender closing the connection after writing headers only; an overly large event payload rejected by an intermediary.","solutions":["Retry the POST from the sender; this is usually a transient transport error.","Check the platform's slog error log for the underlying cause and correlate with proxy logs.","Raise proxy body-size/timeout limits (e.g. nginx client_max_body_size, proxy_read_timeout).","Have the sender send a complete request body and not close the connection prematurely.","If payloads are large, switch to the long-poll/WebSocket connection mode instead of webhook."],"exampleFix":"// before\nreq, _ := http.NewRequest(\"POST\", hookURL, nil) // body never written\nreq.Header.Set(\"Content-Length\", \"1024\")\n// after\nbody, _ := json.Marshal(event)\nreq, _ := http.NewRequest(\"POST\", hookURL, bytes.NewReader(body))\nreq.Header.Set(\"Content-Type\", \"application/json\")","handlingStrategy":"retry","validationCode":"func sanityCheckEvent(ev []byte) error {\n    if len(ev) == 0 { return errors.New(\"event body is empty\") }\n    if !json.Valid(ev) { return errors.New(\"event body is not valid JSON\") }\n    return nil\n}","typeGuard":"func isBodyReadFailure(resp *http.Response) bool { return resp != nil && resp.StatusCode == http.StatusBadRequest }","tryCatchPattern":"err := postWithRetry(hookURL, event, 3, time.Second)\nif err != nil {\n    slog.Warn(\"lark webhook delivery failed after retries\", \"error\", err)\n}","preventionTips":["Retry transient POST failures with backoff — body-read errors are usually transient","Raise reverse-proxy body size and read-timeout limits","Keep event payloads small; prefer long-poll/WS mode for large events","Monitor the platform's slog error log for repeated read failures pointing to a proxy/network issue"],"tags":["webhook","http","body-read","feishu"],"backgroundTag":"http-request-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"}