grpc/grpc-go · error

invalid gRPC request method %q

Error message

invalid gRPC request method %q

What it means

Returned by NewServerHandlerTransport when an incoming HTTP request uses any method other than POST. The gRPC over HTTP/2 wire protocol requires POST for all RPCs; the handler writes a 405 Method Not Allowed and returns this error. The check lives at internal/transport/handler_server.go:54-58.

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 03255a9237)

Solutions

  1. Ensure only POST requests reach the gRPC handler; reject/redirect other methods at the proxy or router layer.
  2. For health checks, point them at a dedicated HTTP health endpoint instead of the gRPC service path.
  3. Handle CORS preflight (OPTIONS) in middleware before the request reaches grpc.Server.ServeHTTP.
  4. If serving gRPC and REST on the same mux, route by method and content-type to the correct handler.

Example fix

// before
http.Handle("/helloworld.Greeter/", grpcServer)
// a GET to that path yields 405 + the error

// after
mux := http.NewServeMux()
mux.HandleFunc("/healthz", healthHandler) // GET health checks land here
mux.Handle("/helloworld.Greeter/", grpcServer) // only POST gRPC here
Defensive patterns

Strategy: validation

Validate before calling

if r.Method != http.MethodPost {
    http.Error(w, "use POST", http.StatusMethodNotAllowed)
    return
}
srv.ServeHTTP(w, r)

Try / catch

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

Prevention

When it happens

Trigger: Routing a non-POST request (GET, PUT, OPTIONS, HEAD) into grpc.Server.ServeHTTP via the http.Handler transport; a health-check or browser preflight hitting the gRPC endpoint; a reverse proxy forwarding the wrong method.

Common situations: Load balancer health checks using GET against the gRPC path; CORS preflight (OPTIONS) reaching the handler; curl without -X POST; misconfigured gateway that rewrites the method.

Related errors


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