chenhg5/cc-connect · warning
read body failed
Error message
read body failed
What it means
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.
Source
Thrown at platform/feishu/feishu.go:730
p.cancel = cancel
p.mu.Unlock()
go func() {
slog.Info(p.tag()+": webhook server listening", "port", p.port, "path", p.callbackPath)
if err := p.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
slog.Error(p.tag()+": webhook server error", "error", err)
}
}()
return nil
}
// webhookHandler handles HTTP webhook requests from Lark international version
func (p *Platform) webhookHandler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
slog.Error(p.tag()+": read webhook body failed", "error", err)
http.Error(w, "read body failed", http.StatusBadRequest)
return
}
// Build EventReq from HTTP request
req := &larkevent.EventReq{
Header: r.Header,
Body: body,
RequestURI: r.RequestURI,
}
// Use the SDK's event dispatcher to handle the request
resp := p.eventHandler.Handle(r.Context(), req)
// Write response
for k, v := range resp.Header {
w.Header()[k] = v
}
w.WriteHeader(resp.StatusCode)View on GitHub (pinned to 4000b2338a)
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.
Example fix
// before
req, _ := http.NewRequest("POST", hookURL, nil) // body never written
req.Header.Set("Content-Length", "1024")
// after
body, _ := json.Marshal(event)
req, _ := http.NewRequest("POST", hookURL, bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json") Defensive patterns
Strategy: retry
Validate before calling
func sanityCheckEvent(ev []byte) error {
if len(ev) == 0 { return errors.New("event body is empty") }
if !json.Valid(ev) { return errors.New("event body is not valid JSON") }
return nil
} Type guard
func isBodyReadFailure(resp *http.Response) bool { return resp != nil && resp.StatusCode == http.StatusBadRequest } Try / catch
err := postWithRetry(hookURL, event, 3, time.Second)
if err != nil {
slog.Warn("lark webhook delivery failed after retries", "error", err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/723790de73985fc0.
Report an issue: GitHub.