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

  1. Rename one of the duplicate HttpFilters so every name in http_filters is unique.
  2. Remove the redundant duplicate filter entry.
  3. 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

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


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/1fc4700474dc54bd. Report an issue: GitHub.