router-for-me/CLIProxyAPI · warning

failed to read plugin management request body

Error message

failed to read plugin management request body

What it means

The plugin host management proxy reads the full request body before forwarding it to a plugin's management handler. This 400 response means io.ReadAll on the request body failed, which almost always indicates the client disconnected mid-request or sent a malformed/chunked body that violated HTTP framing. The body is buffered so it can be replayed to the plugin handler, hence the explicit read step.

Source

Thrown at internal/pluginhost/management.go:246

// ServeManagementHTTP dispatches an authenticated Management API request to a plugin route.
func (h *Host) ServeManagementHTTP(w http.ResponseWriter, r *http.Request) bool {
	if h == nil || w == nil || r == nil || r.URL == nil {
		return false
	}
	key := managementRouteKey(r.Method, r.URL.Path)
	h.mu.Lock()
	record, okRoute := h.managementRoutes[key]
	h.mu.Unlock()
	if !okRoute || record.route.Handler == nil || h.isPluginFused(record.pluginID) {
		return false
	}

	var body []byte
	if r.Body != nil {
		var errRead error
		body, errRead = io.ReadAll(r.Body)
		if errRead != nil {
			http.Error(w, "failed to read plugin management request body", http.StatusBadRequest)
			return true
		}
		if errClose := r.Body.Close(); errClose != nil {
			log.Warnf("pluginhost: failed to close plugin management request body: %v", errClose)
		}
	}
	r.Body = io.NopCloser(bytes.NewReader(body))

	resp, errHandle := h.callManagementHandler(r.Context(), record, pluginapi.ManagementRequest{
		Method:  r.Method,
		Path:    r.URL.Path,
		Headers: cloneHeader(r.Header),
		Query:   cloneValues(r.URL.Query()),
		Body:    bytes.Clone(body),
	})
	if errHandle != nil {
		log.Warnf("pluginhost: management handler %s failed: %v", record.pluginID, errHandle)
		http.Error(w, "plugin management handler failed", http.StatusBadGateway)

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Retry the management request with a well-formed body and correct Content-Length/chunked encoding
  2. Check client-side timeouts and cancellation: increase the HTTP client timeout or avoid cancelling the context while the request is in flight
  3. Inspect any intermediary proxy (nginx, traefik) between client and server for early connection teardown or request buffering limits
  4. Enable trace-level logs to confirm whether the client is resetting the connection (RST) mid-body
Defensive patterns

Strategy: retry

Validate before calling

// Before sending, make sure the body is well-formed and sized:
const body = JSON.stringify(payload);
fetch(url, { method: 'POST', headers: { 'Content-Length': String(Buffer.byteLength(body)) }, body });

Try / catch

// Treat 400-on-read as transient client-side framing failure: verify body integrity, then retry once.
const res = await fetch(url, {...});
if (res.status === 400) {
  const text = await res.text();
  if (text.includes('failed to read plugin management request body')) {
    return retryOnce(sameRequest); // with identical, verified body
  }
  throw new Error(text);
}

Prevention

When it happens

Trigger: Any management API request routed to a plugin management endpoint (a path registered by a plugin) where the client aborts the connection while the body is being transmitted, or where the body is truncated/corrupt (bad Content-Length, broken chunked encoding). Also possible if an intermediary proxy closes the connection early.

Common situations: Client-side timeouts or cancellations (curl aborted, HTTP client context cancelled), flaky connections between a reverse proxy and the server, load-test tools closing keep-alive connections aggressively, or custom scripts sending hand-crafted requests with wrong Content-Length.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/f3b27c983654d736. Report an issue: GitHub.