grpc/grpc-java · error · ResourceInvalidException
Filter chain names must be unique. Found duplicate
Error message
Filter chain names must be unique. Found duplicate: ${filterChainName} What it means
Each FilterChain in a server-side Listener must have a unique non-empty name so gRPC can identify filter chains in logs and match logic. The parser collects names into a set and throws ResourceInvalidException when a duplicate is added. Resources with duplicate filter chain names are considered invalid and rejected.
Solutions
- Give each filter_chain in the Listener a distinct name in the control plane config
- Leave the name field empty (the parser auto-generates unique names 'chain_0', 'chain_1', ...) instead of duplicating names
- Audit the LDS resource JSON/YAML for repeated filter_chain name values and rename them
Example fix
# before filter_chains: - name: fc1 ... - name: fc1 ... # after filter_chains: - name: fc1 ... - name: fc2 ...
Defensive patterns
Strategy: validation
Validate before calling
Set<String> seen = new HashSet<>();
for (FilterChain fc : listener.getFilterChainsList()) {
if (!seen.add(fc.getName())) {
throw new IllegalArgumentException("Duplicate filter chain name: " + fc.getName());
}
} Try / catch
try {
listener = XdsListenerResource.parseServerSideListener(proto, ...);
} catch (ResourceInvalidException e) {
if (e.getMessage().startsWith("Filter chain names must be unique")) {
logger.warn("Rename duplicated filter chain in LDS resource", e);
}
} Prevention
- Enforce unique, meaningful filter chain names at the control plane
- Prefer leaving names empty (auto-generated) over duplicating them
- Include filter chain names in control-plane CI validation
When it happens
Trigger: A Listener proto contains two or more filter_chains entries whose name field is identical (e.g. both set to 'fc1', or both explicitly set to the same string) while processing parseServerSideListener.
Common situations: Copy-pasted filter chain blocks in Envoy-style configs where the name field was left identical; control planes that auto-generate filter chains without unique naming; hand-edited LDS resources.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- A terminal HttpFilter must be the last filter
- AndMatcher must have at least 2 predicates
- client_listener_resource_name_template
- Cluster " + cluster.getName() + ": unspecified cluster…
- common-tls-context is required in upstream-tls-context
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/31bcb856899081b6.
Report an issue: GitHub.
Appendix: source
Thrown at xds/src/main/java/io/grpc/xds/XdsListenerResource.java:195
break;
default:
// noop
}
}
ImmutableList.Builder<FilterChain> filterChains = ImmutableList.builder();
Set<String> filterChainNames = new HashSet<>();
Set<FilterChainMatch> filterChainMatchSet = new HashSet<>();
int i = 0;
for (io.envoyproxy.envoy.config.listener.v3.FilterChain fc : proto.getFilterChainsList()) {
// May be empty. If it's not empty, required to be unique.
String filterChainName = fc.getName();
if (filterChainName.isEmpty()) {
// Generate a name, so we can identify it in the logs.
filterChainName = "chain_" + i;
}
if (!filterChainNames.add(filterChainName)) {
throw new ResourceInvalidException("Filter chain names must be unique. "
+ "Found duplicate: " + filterChainName);
}
filterChains.add(
parseFilterChain(fc, filterChainName, tlsContextManager, filterRegistry,
filterChainMatchSet, certProviderInstances, args));
i++;
}
FilterChain defaultFilterChain = null;
if (proto.hasDefaultFilterChain()) {
String defaultFilterChainName = proto.getDefaultFilterChain().getName();
if (defaultFilterChainName.isEmpty()) {
defaultFilterChainName = "chain_default";
}
defaultFilterChain = parseFilterChain(
proto.getDefaultFilterChain(), defaultFilterChainName, tlsContextManager, filterRegistry,
null, certProviderInstances, args);
}View on GitHub (pinned to 64daddc1f3)