{"record":{"id":"a1af15bb4a7760e3","repo":"grpc/grpc-java","slug":"missing-httpfilter-in-httpconnectionmanager","errorCode":null,"errorMessage":"Missing HttpFilter in HttpConnectionManager.","messagePattern":"Missing HttpFilter in HttpConnectionManager\\.","errorType":"exception","errorClass":"ResourceInvalidException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/XdsListenerResource.java","lineNumber":517,"sourceCode":"      throw new ResourceInvalidException(\n          \"HttpConnectionManager with xff_num_trusted_hops unsupported\");\n    }\n    if (!proto.getOriginalIpDetectionExtensionsList().isEmpty()) {\n      throw new ResourceInvalidException(\"HttpConnectionManager with \"\n          + \"original_ip_detection_extensions unsupported\");\n    }\n    // Obtain max_stream_duration from Http Protocol Options.\n    long maxStreamDuration = 0;\n    if (proto.hasCommonHttpProtocolOptions()) {\n      HttpProtocolOptions options = proto.getCommonHttpProtocolOptions();\n      if (options.hasMaxStreamDuration()) {\n        maxStreamDuration = Durations.toNanos(options.getMaxStreamDuration());\n      }\n    }\n\n    // Parse http filters.\n    if (proto.getHttpFiltersList().isEmpty()) {\n      throw new ResourceInvalidException(\"Missing HttpFilter in HttpConnectionManager.\");\n    }\n    List<Filter.NamedFilterConfig> filterConfigs = new ArrayList<>();\n    Set<String> names = new HashSet<>();\n    for (int i = 0; i < proto.getHttpFiltersCount(); i++) {\n      io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter\n          httpFilter = proto.getHttpFiltersList().get(i);\n      String filterName = httpFilter.getName();\n      if (!names.add(filterName)) {\n        throw new ResourceInvalidException(\n            \"HttpConnectionManager contains duplicate HttpFilter: \" + filterName);\n      }\n      StructOrError<Filter.FilterConfig> filterConfig =\n          parseHttpFilter(httpFilter, filterRegistry, isForClient, args);\n      if ((i == proto.getHttpFiltersCount() - 1)\n          && (filterConfig == null || !isTerminalFilter(filterConfig.getStruct()))) {\n        throw new ResourceInvalidException(\"The last HttpFilter must be a terminal filter: \"\n            + filterName);\n      }","sourceCodeStart":499,"sourceCodeEnd":535,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/XdsListenerResource.java#L499-L535","documentation":"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.","triggerScenarios":"Listener's http_connection_manager with an empty http_filters list, hit at the top of the filter-parsing loop in parseHttpConnectionManager.","commonSituations":"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.","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."],"exampleFix":"// before\nhttp_connection_manager: { route_config: {...} }\n// after\nhttp_connection_manager:\n  route_config: {...}\n  http_filters:\n    - name: envoy.filters.http.router\n      typed_config: { \"@type\": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router }","handlingStrategy":"validation","validationCode":"if (hcm.getHttpFiltersList().isEmpty()) {\n  throw new IllegalArgumentException(\"http_filters must contain at least the router filter\");\n}","typeGuard":null,"tryCatchPattern":"try { applyResource(listener) } catch (ResourceInvalidException e) { if (e.getMessage().equals(\"Missing HttpFilter in HttpConnectionManager.\")) appendDefaultRouterFilter(); }","preventionTips":["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"],"tags":["grpc","xds","http","config-validation"],"backgroundTag":"empty-required-field","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}