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.

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.

Appendix: 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)