grpc/grpc-java · error · ResourceInvalidException
HttpConnectionManager contains duplicate HttpFilter: ${filte
Error message
HttpConnectionManager contains duplicate HttpFilter: ${filterName} What it means
Each HttpFilter name within an HttpConnectionManager must be unique because filter names are used as keys into the per-filter config map. A duplicate name is ambiguous, so XdsListenerResource throws ResourceInvalidException naming the duplicated filter.
Source
Thrown at xds/src/main/java/io/grpc/xds/XdsListenerResource.java:526
if (proto.hasCommonHttpProtocolOptions()) {
HttpProtocolOptions options = proto.getCommonHttpProtocolOptions();
if (options.hasMaxStreamDuration()) {
maxStreamDuration = Durations.toNanos(options.getMaxStreamDuration());
}
}
// Parse http filters.
if (proto.getHttpFiltersList().isEmpty()) {
throw new ResourceInvalidException("Missing HttpFilter in HttpConnectionManager.");
}
List<Filter.NamedFilterConfig> filterConfigs = new ArrayList<>();
Set<String> names = new HashSet<>();
for (int i = 0; i < proto.getHttpFiltersCount(); i++) {
io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter
httpFilter = proto.getHttpFiltersList().get(i);
String filterName = httpFilter.getName();
if (!names.add(filterName)) {
throw new ResourceInvalidException(
"HttpConnectionManager contains duplicate HttpFilter: " + filterName);
}
StructOrError<Filter.FilterConfig> filterConfig =
parseHttpFilter(httpFilter, filterRegistry, isForClient, args);
if ((i == proto.getHttpFiltersCount() - 1)
&& (filterConfig == null || !isTerminalFilter(filterConfig.getStruct()))) {
throw new ResourceInvalidException("The last HttpFilter must be a terminal filter: "
+ filterName);
}
if (filterConfig == null) {
continue;
}
if (filterConfig.getErrorDetail() != null) {
throw new ResourceInvalidException(
"HttpConnectionManager contains invalid HttpFilter: "
+ filterConfig.getErrorDetail());
}
if ((i < proto.getHttpFiltersCount() - 1) && isTerminalFilter(filterConfig.getStruct())) {View on GitHub (pinned to 64daddc1f3)
Solutions
- Rename one of the duplicate HttpFilters so every name in http_filters is unique.
- Remove the redundant duplicate filter entry.
- Fix the control-plane merge logic that concatenated two filter lists without deduplication.
Example fix
// before
http_filters: [{ name: router, ... }, { name: router, ... }]
// after
http_filters: [{ name: envoy.filters.http.router, ... }] Defensive patterns
Strategy: validation
Validate before calling
Set<String> names = new HashSet<>();
for (HttpFilter f : hcm.getHttpFiltersList()) {
if (!names.add(f.getName())) throw new IllegalArgumentException("duplicate HttpFilter: " + f.getName());
} Try / catch
try { applyResource(listener) } catch (ResourceInvalidException e) { if (e.getMessage().contains("duplicate HttpFilter")) dedupeFiltersByName(); } Prevention
- Deduplicate by name when merging filter lists from multiple config sources
- Never emit the same filter twice (router must appear exactly once, last)
- Add a CI lint asserting unique HttpFilter names
When it happens
Trigger: http_filters list containing two HttpFilter entries with the same name field, detected via the names HashSet inside the parse loop in parseHttpConnectionManager.
Common situations: Merging/concatenating filter lists from multiple sources (base + overlay config) without deduplicating names, control-plane merge bugs, or copy-paste of a filter block (e.g. two 'envoy.filters.http.router' entries or two instances of the same named filter).
Related errors
- Missing HttpFilter in HttpConnectionManager.
- The last HttpFilter must be a terminal filter: ${filterName}
- unsupported ExtAuthz service type: only grpc_service is supp
- Invalid ring hash function: " + ringHash.getHashFunction()
- Custom LB config does not contain a JSON object
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/1fc4700474dc54bd.
Report an issue: GitHub.