grpc/grpc-java · error · ResourceInvalidException
HttpConnectionManager contains invalid RDS: missing…
Error message
HttpConnectionManager contains invalid RDS: missing config_source
What it means
An HttpConnectionManager configured with RDS (route discovery service) must supply a config_source telling gRPC where to discover the RouteConfiguration. When the Rds message has no config_source set at all, xDS resource parsing fails with ResourceInvalidException and the listener is rejected.
Solutions
- Add a config_source to the Rds block, e.g. ads: {} for ADS-based discovery
- Alternatively use config_source { self: {} } for the same management server
- If routes are static, inline a route_config in the HttpConnectionManager instead of using RDS
Example fix
// before
rds: { route_config_name: "routes" }
// after
rds: {
route_config_name: "routes"
config_source: { ads: {} }
} Defensive patterns
Strategy: validation
Validate before calling
// Java (proto): check RDS completeness before applying
if (hcm.hasRds() && !hcm.getRds().hasConfigSource()) {
throw new IllegalArgumentException("rds requires config_source (e.g. ads: {})");
} Try / catch
try {
xdsClient.watchResource(LISTENER, name, listenerWatcher);
} catch (ResourceInvalidException e) {
if (e.getMessage().contains("missing config_source")) {
log.error("RDS block lacks config_source; fix control plane template");
}
} Prevention
- Template RDS blocks with config_source { ads: {} } by default
- Lint listener configs for required RDS subfields
- Diff generated protos against a known-good sample
When it happens
Trigger: parseHttpConnectionManager sees proto.hasRds() true but rds.hasConfigSource() false — i.e. an Rds block with only a route_config_name and no config_source field.
Common situations: Envoy listener config omitting config_source under rds; YAML-to-proto conversion dropping the nested field; hand-crafted RDS blocks copied from examples that used ADS implicitly in Envoy but must be explicit here.
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
- A terminal HttpFilter must be the last filter
- AndMatcher must have at least 2 predicates
- client_listener_resource_name_template
- Cluster " + cluster.getName() + ": unspecified cluster…
- common-tls-context is required in upstream-tls-context
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/c79d987df9e7e1c7.
Report an issue: GitHub.
Appendix: source
Thrown at xds/src/main/java/io/grpc/xds/XdsListenerResource.java:561
}
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()) {
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);
}
View on GitHub (pinned to 64daddc1f3)