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
- Add the required terminal filter to http_filters (for gRPC xDS this is the router filter, which must be last).
- Regenerate the Listener resource from the control plane ensuring the default filter chain includes http_filters.
- 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
- Always end http_filters with the router filter
- Use control-plane defaults that never emit empty http_filters
- Sanity-check resources after any serialization/transport step that could drop repeated fields
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
- HttpConnectionManager contains duplicate HttpFilter: ${filte
- 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/a1af15bb4a7760e3.
Report an issue: GitHub.