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
- 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).
- If one chain is intended as the fallback, mark it as the default_filter_chain on the Listener instead of a separate FilterChain.
- Remove the duplicate filter chain entry from the LDS resource.
- 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
- Use default_filter_chain for the catch-all instead of adding a wildcard FilterChain.
- Write a policy check that computes the normalized match key per chain and rejects duplicates.
- When adding a new chain, run the overlap check in CI before publishing the resource.
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
- DownstreamTlsContext in LDS response does not contain a…
- filter missing name field
- security configuration on the server-side does not contain…
- unsupported field 'use_original_dst' is present and set to…
- security configuration on the client-side does not contain…
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)