plandex-ai/plandex · error

Error copying response body

Error message

Error copying response body

What it means

In Plandex's proxy_helper.go, proxyRequest forwards an internal HTTP request to the server instance holding the active model stream and then streams the upstream response back to the client via io.Copy(w, resp.Body). This error is logged and a 500 returned when that copy fails mid-stream — meaning the response to the client could not be fully relayed. It indicates the connection broke while the body was being written, not that the upstream request itself failed.

Source

Thrown at app/server/handlers/proxy_helper.go:104

		http.Error(w, "Error forwarding request", http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	// Copy the response headers and status code
	for name, headers := range resp.Header {
		for _, h := range headers {
			w.Header().Add(name, h)
		}
	}
	w.WriteHeader(resp.StatusCode)

	log.Printf("Proxy forwarded successfully with status code: %d\n", resp.StatusCode)

	// Copy the response body
	if _, err := io.Copy(w, resp.Body); err != nil {
		log.Printf("Error copying response body: %v\n", err)
		http.Error(w, "Error copying response body", http.StatusInternalServerError)
	}
}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check server logs for the underlying io.Copy error and the upstream status code logged just before it — a 200 followed by copy failure means mid-stream disconnect, not a bad request.
  2. Verify the host at modelStream.InternalIp is still alive and the plan's server did not restart; retry the operation so a fresh active stream is resolved.
  3. Check for client-side cancellation: if the user aborted, this server error is expected noise and can be ignored or downgraded in logging.
  4. Increase any intermediary (load balancer / reverse proxy) read and idle timeouts that are shorter than the model stream duration.
  5. Confirm both servers run the same PORT and can reach each other on the internal network (no firewall/NAT dropping long-lived connections).

Example fix

// before
if _, err := io.Copy(w, resp.Body); err != nil {
    log.Printf("Error copying response body: %v\n", err)
    http.Error(w, "Error copying response body", http.StatusInternalServerError)
}
// after
if _, err := io.Copy(w, resp.Body); err != nil {
    // headers/status already sent; can only log — do not attempt http.Error
    if errors.Is(r.Context().Err(), context.Canceled) {
        log.Printf("Client cancelled proxy stream: %v\n", err)
    } else {
        log.Printf("Error copying response body: %v\n", err)
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// client-side: preflight the upstream host before issuing a long proxied/streamed call
resp, err := http.Head("http://" + internalIP + ":" + port + "/healthz")
if err != nil || resp.StatusCode != http.StatusOK {
    return fmt.Errorf("upstream host %s unavailable, re-resolve active stream first", internalIP)
}

Try / catch

// Go: treat copy errors as retryable stream interruptions; check ctx to skip retries on client cancel
if _, err := io.Copy(w, resp.Body); err != nil {
    if r.Context().Err() != nil {
        return // client went away; do not retry or write 500
    }
    log.Printf("stream interrupted, retrying: %v", err)
    // retry once with a fresh request before surfacing an error
}

Prevention

When it happens

Trigger: The upstream server (modelStream.InternalIp host) closes the connection or resets it mid-response; the requesting client disconnects so w (the ResponseWriter) fails writes; a proxy/timeout between the two servers cuts the streamed body short; or resp.Body returns a read error (e.g. unexpected EOF from chunked streaming).

Common situations: A running plan's server container is restarted or killed mid-stream, so the proxied streaming response dies partway; a load balancer's idle/read timeout fires during a long model stream; the CLI client is cancelled (Ctrl-C) and the server-side write to the client errors; internal IP stale after the plan moved to a different host.

Related errors


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