grpc/grpc-go · error

rls: method name %q is not of the form '/service/method

Error message

rls: method name %q is not of the form '/service/method

What it means

At the start of every Pick, rlsPicker verifies the RPC's FullMethodName matches the gRPC convention /service/method (one leading slash and exactly two slashes total via isFullMethodNameValid). If not, it refuses to route because the key-builder lookup (which splits on the last slash) would produce garbage keys. This is a runtime error returned from the picker, not a config error.

Source

Thrown at balancer/rls/picker.go:84

	grpcTarget      string
	metricsRecorder estats.MetricsRecorder
	defaultPolicy   *childPolicyWrapper // Child policy for the default target.
	ctrlCh          *controlChannel     // Control channel to the RLS server.
	maxAge          time.Duration       // Cache max age from LB config.
	staleAge        time.Duration       // Cache stale age from LB config.
	bg              exitIdler
	logger          *internalgrpclog.PrefixLogger
}

// isFullMethodNameValid return true if name is of the form `/service/method`.
func isFullMethodNameValid(name string) bool {
	return strings.HasPrefix(name, "/") && strings.Count(name, "/") == 2
}

// Pick makes the routing decision for every outbound RPC.
func (p *rlsPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
	if name := info.FullMethodName; !isFullMethodNameValid(name) {
		return balancer.PickResult{}, fmt.Errorf("rls: method name %q is not of the form '/service/method", name)
	}

	// Build the request's keys using the key builders from LB config.
	md, _ := metadata.FromOutgoingContext(info.Ctx)
	reqKeys := p.kbm.RLSKey(md, p.origEndpoint, info.FullMethodName)

	p.lb.cacheMu.Lock()
	var pr balancer.PickResult
	var err error

	// Record metrics without the cache mutex held, to prevent lock contention
	// between concurrent RPC's and their Pick calls. Metrics Recording can
	// potentially be expensive.
	metricsCallback := func() {}
	defer func() {
		p.lb.cacheMu.Unlock()
		metricsCallback()
	}()

View on GitHub (pinned to 03255a9237)

Solutions

  1. Ensure the RPC is invoked through a normally-generated gRPC client stub so FullMethodName is /package.Service/Method.
  2. If proxying, normalize the method to /service/method before it reaches the picker.
  3. Check for interceptors/transformers that strip or mangle the leading slash.

Example fix

// before: invoking with a malformed method
conn.NewConn().Invoke(ctx, "Get", req, resp)
// after: use the generated stub (method name is /pkg.Svc/Get)
client := pb.NewSvcClient(conn)
client.Get(ctx, req)
Defensive patterns

Strategy: try-catch

Validate before calling

func isValidFullMethodName(name string) bool {
    return strings.HasPrefix(name, "/") && strings.Count(name, "/") == 2
}

Try / catch

err := conn.Invoke(ctx, method, req, resp)
if err != nil && strings.Contains(err.Error(), "is not of the form '/service/method") {
    // method name is malformed; re-route through a generated stub or normalize method
    log.Printf("malformed method name %q; falling back", method)
}

Prevention

When it happens

Trigger: Calling a gRPC method whose FullMethodName is not of the form /service/method — e.g. "Get", "service/method" (missing leading slash), or "/a/b/c" (three slashes). Typically only seen with custom invocation handlers, in-process servers with malformed method registration, or proxying tooling that fabricates method names.

Common situations: Using grpc.NewServer with a service whose method name was registered oddly; hand-built streaming proxies; test harnesses that invoke Pick directly with an arbitrary string; custom resolver/interceptor rewriting FullMethodName.

Related errors


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