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

  1. Retry the POST from the sender; this is usually a transient transport error.
  2. Check the platform's slog error log for the underlying cause and correlate with proxy logs.
  3. Raise proxy body-size/timeout limits (e.g. nginx client_max_body_size, proxy_read_timeout).
  4. Have the sender send a complete request body and not close the connection prematurely.
  5. 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

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.