plandex-ai/plandex · error

Error reading request body

Error message

Error reading request body

What it means

TellPlanHandler reads the whole HTTP request body with io.ReadAll before unmarshalling the TellPlanRequest. If the body stream fails mid-read (client disconnect, network reset, proxy/timeout cutting the connection), it logs the error, sends an async Sentry-style notification, and responds 500 with 'Error reading request body'. This is a transport-level failure, not a data problem.

Source

Thrown at app/server/handlers/plans_exec.go:53

	log.Println("planId: ", planId)

	plan := authorizePlanExecUpdate(w, planId, auth)
	if plan == nil {
		return
	}

	settings, err := db.GetPlanSettings(plan)
	if err != nil {
		log.Printf("Error getting plan settings: %v\n", err)
		http.Error(w, "Error getting plan settings", http.StatusInternalServerError)
		return
	}

	body, err := io.ReadAll(r.Body)
	if err != nil {
		log.Printf("Error reading request body: %v\n", err)
		go notify.NotifyErr(notify.SeverityError, fmt.Errorf("error reading request body: %v", err))
		http.Error(w, "Error reading request body", http.StatusInternalServerError)
		return
	}
	defer func() {
		log.Println("Closing request body")
		r.Body.Close()
	}()

	var requestBody shared.TellPlanRequest
	if err := json.Unmarshal(body, &requestBody); err != nil {
		log.Printf("Error parsing request body: %v\n", err)
		go notify.NotifyErr(notify.SeverityError, fmt.Errorf("error parsing request body: %v", err))
		http.Error(w, "Error parsing request body", http.StatusBadRequest)
		return
	}

	_, apiErr := hooks.ExecHook(hooks.WillTellPlan, hooks.HookParams{
		Auth: auth,
		Plan: plan,

View on GitHub (pinned to e2d772072e)

Solutions

  1. Retry the request from the client once the connection is stable; verify the client process was not killed mid-send.
  2. Check client-side: ensure the HTTP client sends Content-Length / closes the request body correctly and does not hang on a slow network.
  3. Inspect and raise proxy/ingress read timeouts (nginx proxy_read_timeout, ALB idle timeout) if large bodies are involved.
  4. Check server logs for the wrapped io error (e.g. 'unexpected EOF', 'connection reset by peer') to confirm transport vs. server fault.

Example fix

// before: no size/timeout guard, raw io.ReadAll
body, err := io.ReadAll(r.Body)
// after: bound the read with http.MaxBytesReader and a server ReadTimeout
r.Body = http.MaxBytesReader(w, r.Body, 10<<20)
body, err := io.ReadAll(r.Body)
if err != nil {
    http.Error(w, "Error reading request body", http.StatusBadRequest)
    return
}
Defensive patterns

Strategy: retry

Validate before calling

// client-side pre-check before sending
if len(payload) > 10<<20 { return errors.New("payload too large") }
req, _ := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(payload))
req.ContentLength = int64(len(payload))

Try / catch

res, err := client.Do(req)
if err != nil || res.StatusCode == 500 {
    if isTransient(err, res) && attempt < 3 { time.Sleep(backoff); continue }
    return fmt.Errorf("body read failed on server: %v", err)
}

Prevention

When it happens

Trigger: POST to the plan tell endpoint where r.Body cannot be fully read: client closed the connection before finishing the upload, an intermediary (load balancer, proxy) reset the stream, or the request exceeded a server/body timeout while reading.

Common situations: CLI client aborts with Ctrl-C mid-request; slow uploads hitting an ingress read timeout; flaky mobile/VPN connections; misconfigured proxy buffering that truncates large bodies.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/1315d0b1ac4c87ad. Report an issue: GitHub.