grpc/grpc-java · error · ResourceInvalidException

Missing HttpFilter in HttpConnectionManager.

Error message

Missing HttpFilter in HttpConnectionManager.

What it means

The xDS API requires every HttpConnectionManager to have at least one HTTP filter, and the final filter must be terminal (e.g. the gRPC router filter). An empty http_filters list is invalid, so XdsListenerResource throws ResourceInvalidException before processing any filters.

Source

Thrown at xds/src/main/java/io/grpc/xds/XdsListenerResource.java:517

      throw new ResourceInvalidException(
          "HttpConnectionManager with xff_num_trusted_hops unsupported");
    }
    if (!proto.getOriginalIpDetectionExtensionsList().isEmpty()) {
      throw new ResourceInvalidException("HttpConnectionManager with "
          + "original_ip_detection_extensions unsupported");
    }
    // Obtain max_stream_duration from Http Protocol Options.
    long maxStreamDuration = 0;
    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);
      }

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Add the required terminal filter to http_filters (for gRPC xDS this is the router filter, which must be last).
  2. Regenerate the Listener resource from the control plane ensuring the default filter chain includes http_filters.
  3. Inspect the resource in the xDS response to confirm filters were not dropped during serialization.

Example fix

// before
http_connection_manager: { route_config: {...} }
// after
http_connection_manager:
  route_config: {...}
  http_filters:
    - name: envoy.filters.http.router
      typed_config: { "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router }
Defensive patterns

Strategy: validation

Validate before calling

if (hcm.getHttpFiltersList().isEmpty()) {
  throw new IllegalArgumentException("http_filters must contain at least the router filter");
}

Try / catch

try { applyResource(listener) } catch (ResourceInvalidException e) { if (e.getMessage().equals("Missing HttpFilter in HttpConnectionManager.")) appendDefaultRouterFilter(); }

Prevention

When it happens

Trigger: Listener's http_connection_manager with an empty http_filters list, hit at the top of the filter-parsing loop in parseHttpConnectionManager.

Common situations: Hand-written bootstrap YAML that forgot the router filter, control-plane generators omitting filters when no routes are configured, or filters stripped by config sanitizers/tampering in transit.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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