googleapis/mcp-toolbox · error

Failed to encode PRM response

Error message

Failed to encode PRM response

What it means

In the Protected Resource Metadata (PRM) HTTP handler, after building the JSON response the server tries to json.Encode it into the http.ResponseWriter. If writing to the response stream fails (client disconnected, broken pipe, TLS/network error mid-response), Encode returns an error and the server logs "Failed to encode PRM response" and attempts a 500 response.

Source

Thrown at internal/server/mcp.go:913

	for _, authSvc := range s.PrimitiveMgr.AuthServices() {
		if mSvc, ok := authSvc.(auth.MCPAuthService); ok && mSvc.IsMCPEnabled() {
			server = mSvc.GetAuthorizationServer()
			scopes = mSvc.GetScopesRequired()
			break
		}
	}

	res := prmResponse{
		Resource:               s.toolboxUrl,
		AuthorizationServers:   []string{server},
		ScopesSupported:        scopes,
		BearerMethodsSupported: []string{"header"},
	}

	w.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(res); err != nil {
		s.logger.ErrorContext(r.Context(), fmt.Sprintf("Failed to encode PRM response: %v", err))
		http.Error(w, "Failed to encode PRM response", http.StatusInternalServerError)
	}
}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Retry the discovery request from the client; this is usually a transient transport failure
  2. Increase the client/proxy read timeout so the PRM response can complete
  3. Check server logs for the underlying encoding error detail to confirm client disconnect vs server issue
  4. Verify network path (LB, ingress) isn't terminating responses early

Example fix

// before (client side)
const res = await fetch(url);
// after (client side, with timeout headroom)
const res = await fetch(url, { signal: AbortSignal.timeout(30000) });
Defensive patterns

Strategy: retry

Validate before calling

// client: check connectivity before fetching PRM
const url = new URL(baseUrl + '/.well-known/oauth-protected-resource');
if (!['http:', 'https:'].includes(url.protocol)) throw new Error('bad base URL');

Try / catch

// client side
try {
  const res = await fetch(prmUrl, { signal: AbortSignal.timeout(30000) });
  if (!res.ok) throw new Error(`PRM request failed: ${res.status}`);
  const prm = await res.json();
} catch (e) {
  if (e.name === 'TimeoutError' || e.name === 'AbortError') retryFetch(prmUrl);
  else throw e;
}

Prevention

When it happens

Trigger: A client closes the connection or times out while the PRM endpoint (e.g. GET /.well-known/oauth-protected-resource) is streaming the JSON body; the underlying TCP connection errors during Write.

Common situations: Aggressive client timeouts in OAuth discovery flows; load balancers closing idle keep-alive connections; client cancels the request right after headers are sent; proxy buffering limits.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/d2607e5e8cba3930. Report an issue: GitHub.