grpc/grpc-java · error · ResourceInvalidException
HttpConnectionManager neither has inlined route_config nor…
Error message
HttpConnectionManager neither has inlined route_config nor RDS
What it means
An HttpConnectionManager must provide routing information either as an inlined route_config or via an Rds reference. When neither field is present the resource is structurally incomplete, so xDS parsing throws ResourceInvalidException and the listener is discarded as invalid.
Solutions
- Add an inline route_config with virtual_hosts to the HttpConnectionManager
- Or set rds { route_config_name: ..., config_source: { ads: {} } } to discover routes dynamically
- Inspect the management server output to confirm it is populating routing fields
Example fix
// before
http_connection_manager: { http_filters: [...] }
// after
http_connection_manager: {
rds: { route_config_name: "routes", config_source: { ads: {} } }
http_filters: [...]
} Defensive patterns
Strategy: validation
Validate before calling
// Java (proto): HCM must have inlined route_config or rds
if (!hcm.hasRouteConfig() && !hcm.hasRds()) {
throw new IllegalArgumentException("HttpConnectionManager needs route_config or rds");
} Try / catch
try {
xdsClient.watchResource(LISTENER, name, listenerWatcher);
} catch (ResourceInvalidException e) {
log.error("HCM missing routing config: {}", e.getMessage());
} Prevention
- Always populate either route_config or rds in HttpConnectionManager
- Add a control-plane smoke test asserting HCM completeness
- Reject half-populated HCM stubs before publishing
When it happens
Trigger: parseHttpConnectionManager reaches the fall-through throw: the HttpConnectionManager proto has neither route_config (inlined) nor rds set.
Common situations: Minimal hand-written listener configs that only set http_filters; control planes emitting HCM stubs before filling routing; truncated or partially merged protobuf configs.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- FilterChainMatch must be unique. Found duplicate
- OnMatch must have either matcher or action
- A terminal HttpFilter must be the last filter
- AndMatcher must have at least 2 predicates
- client_listener_resource_name_template
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/af2779d38b1a8773.
Report an issue: GitHub.
Appendix: source
Thrown at xds/src/main/java/io/grpc/xds/XdsListenerResource.java:571
List<VirtualHost> virtualHosts = extractVirtualHosts(
proto.getRouteConfig(), filterRegistry, args);
return io.grpc.xds.HttpConnectionManager.forVirtualHosts(
maxStreamDuration, virtualHosts, filterConfigs);
}
if (proto.hasRds()) {
Rds rds = proto.getRds();
if (!rds.hasConfigSource()) {
throw new ResourceInvalidException(
"HttpConnectionManager contains invalid RDS: missing config_source");
}
if (!rds.getConfigSource().hasAds() && !rds.getConfigSource().hasSelf()) {
throw new ResourceInvalidException(
"HttpConnectionManager contains invalid RDS: must specify ADS or self ConfigSource");
}
return io.grpc.xds.HttpConnectionManager.forRdsName(
maxStreamDuration, rds.getRouteConfigName(), filterConfigs);
}
throw new ResourceInvalidException(
"HttpConnectionManager neither has inlined route_config nor RDS");
}
// hard-coded: currently router config is the only terminal filter.
private static boolean isTerminalFilter(Filter.FilterConfig filterConfig) {
return RouterFilter.ROUTER_CONFIG.equals(filterConfig);
}
@VisibleForTesting
@Nullable // Returns null if the filter is optional but not supported.
static StructOrError<Filter.FilterConfig> parseHttpFilter(
io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter
httpFilter, FilterRegistry filterRegistry, boolean isForClient,
XdsResourceType.Args args) {
String filterName = httpFilter.getName();
boolean isOptional = httpFilter.getIsOptional();
if (!httpFilter.hasTypedConfig()) {
return isOptional ? null : StructOrError.fromError(View on GitHub (pinned to 64daddc1f3)