grpc/grpc-go · error

multiple filter chains with overlapping matching rules are…

Error message

multiple filter chains with overlapping matching rules are defined

What it means

Returned by addFilterChainsForSourcePorts when a new filter chain has no source_ports specified (so it would match any source port via the wildcard slot 0) but that wildcard slot is already occupied by a previously-seen filter chain with the same destination prefix, server name, source prefix, and no source ports. grpc-go cannot non-deterministically pick between two equally-matching chains, so it rejects the LDS resource.

Solutions

  1. Disambiguate the filter chains: give each a distinct match criterion (e.g. distinct server_names, distinct source_ports, distinct destination prefix, or distinct source prefix).
  2. If one chain is intended as the fallback, mark it as the default_filter_chain on the Listener instead of a separate FilterChain.
  3. Remove the duplicate filter chain entry from the LDS resource.
  4. Audit the control-plane translation logic that produced overlapping chain matches.

Example fix

// before: two chains both match all ports
//   filter_chains: [ { filter_chain_match: { source_prefix_ranges: [{address_prefix:"10.0.0.0",prefix_len:8}] }, ... },
//                    { filter_chain_match: { source_prefix_ranges: [{address_prefix:"10.0.0.0",prefix_len:8}] }, ... } ]
// after: differentiate via source_ports or use default_filter_chain
//   filter_chains: [ { filter_chain_match: { source_prefix_ranges: [...], source_ports: [443] }, ... } ],
//   default_filter_chain: { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Detect filter-chain match overlaps (wildcard-port case) before sending LDS to grpc-go.
func detectChainOverlap(chains []*envoy_listener_pb.FilterChain) error {
    type key struct{ dst, src, sni string }
    seen := map[key]bool{}
    for _, c := range chains {
        m := c.GetFilterChainMatch()
        if len(m.GetSourcePorts()) > 0 { continue }
        k := key{dst: cidrsToString(m.GetPrefixRanges()), src: cidrsToString(m.GetSourcePrefixRanges()), sni: strings.Join(m.GetServerNames(), ",")}
        if seen[k] { return fmt.Errorf("two chains collapse to the same match: %+v", k) }
        seen[k] = true
    }
    return nil
}

Try / catch

Catch in LDS watcher; the resource is rejected until the control plane disambiguates. Log the listener name so the control-plane team can locate the duplicate.

Prevention

When it happens

Trigger: An LDS Listener defines two (or more) server filter chains whose filter_chain_match criteria collapse to the same key (same destination prefix, same source prefix/CIDR, same server name, neither specifying source_ports). Both would match the same inbound connection.

Common situations: Control plane generates multiple filter chains for different SNI/ports but two of them end up with identical match criteria after normalization. Duplicated/mirrored filter chain templates. Migration artifact where a new chain was added without removing the old default one.

Related errors


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

Appendix: source

Thrown at internal/xds/xdsclient/xdsresource/unmarshal_lds.go:598

	// Not found, create a new entry.
	srcPrefixes.Entries = append(srcPrefixes.Entries, SourcePrefixEntry{
		Prefix:  prefix,
		PortMap: make(map[int]NetworkFilterChainConfig),
	})
	return addFilterChainsForSourcePorts(&srcPrefixes.Entries[len(srcPrefixes.Entries)-1], fc)
}

func addFilterChainsForSourcePorts(entry *SourcePrefixEntry, fc *v3listenerpb.FilterChain) error {
	ports := fc.GetFilterChainMatch().GetSourcePorts()
	srcPorts := make([]int, 0, len(ports))
	for _, port := range ports {
		srcPorts = append(srcPorts, int(port))
	}

	if len(srcPorts) == 0 {
		if !entry.PortMap[0].IsEmpty() {
			return errors.New("multiple filter chains with overlapping matching rules are defined")
		}
		fcc, err := filterChainFromProto(fc)
		if err != nil {
			return err
		}
		entry.PortMap[0] = fcc
		return nil
	}
	for _, port := range srcPorts {
		if !entry.PortMap[port].IsEmpty() {
			return errors.New("multiple filter chains with overlapping matching rules are defined")
		}
		fcc, err := filterChainFromProto(fc)
		if err != nil {
			return err
		}
		entry.PortMap[port] = fcc
	}

View on GitHub (pinned to 0c51461d27)