grpc/grpc-go · warning

invalid gRPC request method

Error message

invalid gRPC request method %q

What it means

Returned by NewServerHandlerTransport when the incoming HTTP request method is not POST. The gRPC protocol spec mandates POST for all RPC calls. The transport sets an 'Allow: POST' response header and returns HTTP 405 Method Not Allowed. This path is used when embedding a gRPC server inside a standard net/http handler via grpc.NewServer with a HandlerTransport.

Solutions

  1. Ensure clients use POST for gRPC calls (standard gRPC clients always do).
  2. If health-checking, use grpc health protocol or configure the health checker to use POST.
  3. Route non-POST requests away from the gRPC handler in your HTTP mux.
  4. Verify reverse proxies and load balancers preserve the POST method.

Example fix

// before: health check sends GET to gRPC endpoint
curl http://localhost:8080/myapp.Service/Method
// after: use POST with proper headers
curl -X POST -H "Content-Type: application/grpc" \
  http://localhost:8080/myapp.Service/Method

// or route non-gRPC requests separately:
mux.HandleFunc("/health", healthHandler) // GET health check
mux.Handle("/", grpcServer) // gRPC handler for everything else
Defensive patterns

Strategy: validation

Validate before calling

// Route non-POST requests away from the gRPC handler.
mux := http.NewServeMux()
mux.HandleFunc("/health", healthHandler) // GET health checks
mux.Handle("/", grpcServer)             // gRPC handler
// Standard gRPC clients always POST, so this is sufficient.

Try / catch

// NewServerHandlerTransport returns the error; handle it in your HTTP handler:
st, err := transport.NewServerHandlerTransport(w, r, stats, pool)
if err != nil {
    // it already wrote the HTTP error response
    return
}

Prevention

When it happens

Trigger: An HTTP request reaches the gRPC handler transport (e.g., via http.HandleFunc wrapping grpc server's ServeHTTP) with a method other than POST—GET, PUT, DELETE, etc. The check at handler_server.go:54-58 fires.

Common situations: A browser or curl sending a GET to the gRPC endpoint; a health-check probe using GET; a reverse proxy rewriting methods; HTTP/1.1 client accidentally hitting the gRPC handler; load balancer health checks not using POST.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/af5d997fb911117c. Report an issue: GitHub.

Appendix: source

Thrown at internal/transport/handler_server.go:58

	"google.golang.org/grpc/internal/grpclog"
	"google.golang.org/grpc/internal/grpcutil"
	"google.golang.org/grpc/mem"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"
	"google.golang.org/grpc/stats"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/proto"
)

// 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)

View on GitHub (pinned to 0c51461d27)