grpc/grpc-go · error

invalid gRPC request content-type %q

Error message

invalid gRPC request content-type %q

What it means

Returned by NewServerHandlerTransport when the request's Content-Type header is not a valid gRPC content type (application/grpc or application/grpc+<subtype>). The handler writes a 415 Unsupported Media Type and returns the error, see internal/transport/handler_server.go:60-66.

Source

Thrown at internal/transport/handler_server.go:66

)

// NewServerHandlerTransport returns a ServerTransport handling gRPC from
// inside an http.Handler, or writes an HTTP error to w and returns an error.
// It requires that the http Server supports HTTP/2.
func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats stats.Handler, bufferPool mem.BufferPool) (ServerTransport, error) {
	if r.Method != http.MethodPost {
		w.Header().Set("Allow", http.MethodPost)
		msg := fmt.Sprintf("invalid gRPC request method %q", r.Method)
		http.Error(w, msg, http.StatusMethodNotAllowed)
		return nil, errors.New(msg)
	}
	contentType := r.Header.Get("Content-Type")
	// TODO: do we assume contentType is lowercase? we did before
	contentSubtype, validContentType := grpcutil.ContentSubtype(contentType)
	if !validContentType {
		msg := fmt.Sprintf("invalid gRPC request content-type %q", contentType)
		http.Error(w, msg, http.StatusUnsupportedMediaType)
		return nil, errors.New(msg)
	}
	if r.ProtoMajor != 2 {
		msg := "gRPC requires HTTP/2"
		http.Error(w, msg, http.StatusHTTPVersionNotSupported)
		return nil, errors.New(msg)
	}
	if _, ok := w.(http.Flusher); !ok {
		msg := "gRPC requires a ResponseWriter supporting http.Flusher"
		http.Error(w, msg, http.StatusInternalServerError)
		return nil, errors.New(msg)
	}

	var localAddr net.Addr
	if la := r.Context().Value(http.LocalAddrContextKey); la != nil {
		localAddr, _ = la.(net.Addr)
	}
	var authInfo credentials.AuthInfo
	if r.TLS != nil {

View on GitHub (pinned to 03255a9237)

Solutions

  1. Send requests with Content-Type: application/grpc (or application/grpc+proto) from a real gRPC client.
  2. Fix proxy/gateway config to preserve the original Content-Type header end-to-end.
  3. For grpc-web, ensure the gateway translates the content type correctly before forwarding.
  4. Verify with curl: curl -X POST -H 'Content-Type: application/grpc' ...

Example fix

// before
curl http://svc/Service/Method -H 'Content-Type: application/json'

// after
curl http://svc/Service/Method --http2-prior-knowledge \
  -H 'Content-Type: application/grpc' -H 'TE: trailers'
Defensive patterns

Strategy: validation

Validate before calling

ct := r.Header.Get("Content-Type")
if _, ok := grpcutil.ContentSubtype(ct); !ok {
    http.Error(w, "expect application/grpc", http.StatusUnsupportedMediaType)
    return
}

Type guard

func isGRPCContentType(ct string) bool { _, ok := grpcutil.ContentSubtype(ct); return ok }

Try / catch

if _, err := transport.NewServerHandlerTransport(w, r, stats, pool); err != nil {
    log.Printf("rejected non-gRPC request: %v", err)
}

Prevention

When it happens

Trigger: Sending a request with a missing or wrong Content-Type (e.g. application/json, text/plain) to the gRPC http.Handler transport; a proxy stripping or rewriting the Content-Type header.

Common situations: Calling the endpoint with a plain HTTP/JSON client instead of a gRPC client; grpc-web transcoding gateway not setting Content-Type; a service mesh mutating headers; forgetting Content-Type in a manual curl.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/4b387ba61b056b17. Report an issue: GitHub.