grpc/grpc-go · error

rls: GrpcKeyBuilder in RouteLookupConfig has required_match

Error message

rls: GrpcKeyBuilder in RouteLookupConfig has required_match field set {%+v}

What it means

Each header entry in a GrpcKeyBuilder has a required_match flag. The RLS key-builder semantics do not support conditional (required) header matching — all configured header keys are extracted unconditionally — so MakeBuilderMap rejects any header with required_match==true.

Source

Thrown at balancer/rls/internal/keys/builder.go:56

	kbs := cfg.GetGrpcKeybuilders()
	if len(kbs) == 0 {
		return nil, errors.New("rls: RouteLookupConfig does not contain any GrpcKeyBuilder")
	}

	bm := make(map[string]builder)
	for _, kb := range kbs {
		// Extract keys from `headers`, `constant_keys` and `extra_keys` fields
		// and populate appropriate values in the builder struct. Also ensure
		// that keys are not repeated.
		var matchers []matcher
		seenKeys := make(map[string]bool)
		constantKeys := kb.GetConstantKeys()
		for k := range kb.GetConstantKeys() {
			seenKeys[k] = true
		}
		for _, h := range kb.GetHeaders() {
			if h.GetRequiredMatch() {
				return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig has required_match field set {%+v}", kbs)
			}
			key := h.GetKey()
			if seenKeys[key] {
				return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q across headers, constant_keys and extra_keys {%+v}", key, kbs)
			}
			seenKeys[key] = true
			matchers = append(matchers, matcher{key: h.GetKey(), names: h.GetNames()})
		}
		if seenKeys[kb.GetExtraKeys().GetHost()] {
			return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q in extra_keys from constant_keys or headers {%+v}", kb.GetExtraKeys().GetHost(), kbs)
		}
		if seenKeys[kb.GetExtraKeys().GetService()] {
			return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q in extra_keys from constant_keys or headers {%+v}", kb.GetExtraKeys().GetService(), kbs)
		}
		if seenKeys[kb.GetExtraKeys().GetMethod()] {
			return nil, fmt.Errorf("rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q in extra_keys from constant_keys or headers {%+v}", kb.GetExtraKeys().GetMethod(), kbs)
		}
		b := builder{

View on GitHub (pinned to 03255a9237)

Solutions

  1. Remove required_match (or set it false) from every header in every GrpcKeyBuilder.
  2. If you need presence-based routing, handle it upstream of RLS or use distinct key builders per path.
  3. Audit all grpc_keybuilders[].headers entries for the field.

Example fix

// before
"headers": [{ "key": "region", "names": ["x-region"], "required_match": true }]
// after
"headers": [{ "key": "region", "names": ["x-region"] }]
Defensive patterns

Strategy: validation

Validate before calling

func validateNoRequiredMatch(cfg *rlspb.RouteLookupConfig) error {
    for bi, kb := range cfg.GetGrpcKeybuilders() {
        for hi, h := range kb.GetHeaders() {
            if h.GetRequiredMatch() {
                return fmt.Errorf("keybuilder[%d].header[%d] has required_match set", bi, hi)
            }
        }
    }
    return nil
}

Prevention

When it happens

Trigger: A grpc_keybuilders[].headers[] entry with "required_match": true (proto HeaderMatcher.required_match). E.g. {"key":"region","names":["x-region"],"required_match":true}.

Common situations: Reusing HeaderMatcher structs from gRPC service config (where required_match is valid) inside an RLS key builder; translating routing rules verbatim into RLS key builders.

Related errors


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