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
- Ensure clients use POST for gRPC calls (standard gRPC clients always do).
- If health-checking, use grpc health protocol or configure the health checker to use POST.
- Route non-POST requests away from the gRPC handler in your HTTP mux.
- 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
- Ensure all gRPC clients use POST (standard gRPC clients do).
- Route health checks and non-gRPC traffic to separate HTTP handlers.
- Verify reverse proxies preserve the POST method.
- Log unexpected HTTP methods hitting the gRPC endpoint.
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
- gRPC requires a ResponseWriter supporting http.Flusher
- invalid gRPC request content-type
- gRPC requires HTTP/2
- transport: set send compressor called after headers sent or…
- buffer size is not an exponent of two
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)