grpc/grpc-java · error · ResourceInvalidException

HttpConnectionManager contains invalid HttpFilter

Error message

HttpConnectionManager contains invalid HttpFilter: ${filterConfig.getErrorDetail()}

What it means

One of the HttpFilter entries in the HttpConnectionManager failed to parse: parseHttpFilter returned a StructOrError carrying an error detail (unknown filter type, invalid typed_config, unsupported config). XdsListenerResource surfaces that error detail as a ResourceInvalidException prefixed with 'HttpConnectionManager contains invalid HttpFilter:'.

Solutions

  1. Read the embedded errorDetail in the message to identify which filter and field failed, then fix that filter's typed_config.
  2. Remove filters unsupported by gRPC xDS (keep only router and other registered gRPC filters); do Envoy-specific filtering at the Envoy proxy.
  3. Verify the filter's @type URL and config schema against the gRPC version in use; upgrade the library if the filter should be supported.

Example fix

// before: Envoy-only filter gRPC cannot parse
http_filters:
  - name: envoy.filters.http.lua
    typed_config: { "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua, ... }
// after
http_filters:
  - name: envoy.filters.http.router
    typed_config: { "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router }
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check each filter against the registry before submission
for (HttpFilter f : hcm.getHttpFiltersList()) {
  if (!KNOWN_GRPC_HTTP_FILTERS.contains(f.getName()))
    throw new IllegalArgumentException("filter not supported by grpc xds: " + f.getName());
}

Try / catch

try { applyResource(listener) } catch (ResourceInvalidException e) { if (e.getMessage().startsWith("HttpConnectionManager contains invalid HttpFilter")) { logDetail(e.getMessage()); dropUnparsableFiltersAndResubmit(); } }

Prevention

When it happens

Trigger: During the http_filters loop in parseHttpConnectionManager, filterConfig.getErrorDetail() != null — i.e. the filter's typed_config references an unknown filter name, has a malformed Any/Struct config, or the filter's own config parser rejected the values.

Common situations: Filters the gRPC filter registry does not recognize (Envoy-only filters like http lua or ext_proc), typed_config @type URLs that don't match the registered filter, or config fields invalid for the specific filter version.

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


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

Appendix: source

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

      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())) {
        throw new ResourceInvalidException("A terminal HttpFilter must be the last filter: "
            + filterName);
      }
      filterConfigs.add(new Filter.NamedFilterConfig(filterName, filterConfig.getStruct()));
    }

    // Parse inlined RouteConfiguration or RDS.
    if (proto.hasRouteConfig()) {
      List<VirtualHost> virtualHosts = extractVirtualHosts(
          proto.getRouteConfig(), filterRegistry, args);
      return io.grpc.xds.HttpConnectionManager.forVirtualHosts(
          maxStreamDuration, virtualHosts, filterConfigs);
    }
    if (proto.hasRds()) {

View on GitHub (pinned to 64daddc1f3)