grpc/grpc-java · error · ResourceInvalidException
RouteConfiguration contains invalid virtual host
Error message
RouteConfiguration contains invalid virtual host: ${virtualHost.getErrorDetail()} What it means
One of the RouteConfiguration's virtual hosts failed validation: parseVirtualHost returned a StructOrError carrying an errorDetail (e.g. invalid domains, bad route match, or unsupported route action). The whole RouteConfiguration is rejected because every virtual host must be valid.
Solutions
- Read the embedded errorDetail in the message — it names the exact vhost validation failure
- Fix the offending virtual host (domains, routes, or action) per the detail
- Remove or simplify routes using unsupported Envoy features
- Validate the RouteConfiguration against gRPC xDS-supported route semantics before deployment
Example fix
// before
virtual_hosts: [{name: "a", domains: [""], routes: [{}]}] // empty route action
// after
virtual_hosts: [{
name: "a",
domains: ["example.com"],
routes: [{match: {prefix: "/"}, route: {cluster: "service"}}]
}] Defensive patterns
Strategy: validation
Validate before calling
// Pre-validate each virtual host minimally
for (VirtualHostProto vh : rc.getVirtualHostsList()) {
if (vh.getDomainsList().isEmpty() || vh.getRoutesList().isEmpty()) {
throw new IllegalArgumentException("vhost " + vh.getName() + " missing domains/routes");
}
} Try / catch
try {
xdsClient.watchResource(ROUTE_CONFIGURATION, name, watcher);
} catch (ResourceInvalidException e) {
// message embeds the per-vhost errorDetail
log.error("Invalid vhost: {}", e.getMessage());
} Prevention
- Read the embedded errorDetail — it pinpoints the failing vhost and reason
- Restrict routes to gRPC-supported actions (route/cluster, weighted clusters)
- Test route configs against gRPC xDS before Envoy-wide rollout
When it happens
Trigger: extractVirtualHosts parsing routeConfig.getVirtualHostsList(); parseVirtualHost returns non-null errorDetail — e.g. a vhost with empty/overlapping domains, routes with no matching action, or invalid cluster_specifier_plugin references.
Common situations: Route configs from Envoy using features gRPC xDS doesn't support (redirect, direct_response nuances, weighted clusters); duplicate/invalid domains in vhosts; routes referencing nonexistent plugin names.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Multiple ClusterSpecifierPlugins with the same name
- A terminal HttpFilter must be the last filter
- Address is not an IP
- All xds transports for authority are in backoff
- AndMatcher must have at least 2 predicates
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/fe238916a27f33bf.
Report an issue: GitHub.
Appendix: source
Thrown at xds/src/main/java/io/grpc/xds/XdsRouteConfigureResource.java:173
if (pluginConfig != null) {
if (pluginConfigMap.put(pluginName, pluginConfig) != null) {
throw new ResourceInvalidException(
"Multiple ClusterSpecifierPlugins with the same name: " + pluginName);
}
} else {
// The plugin parsed successfully, and it's not supported, but it's marked as optional.
optionalPlugins.add(pluginName);
}
}
}
List<VirtualHost> virtualHosts = new ArrayList<>(routeConfig.getVirtualHostsCount());
for (io.envoyproxy.envoy.config.route.v3.VirtualHost virtualHostProto
: routeConfig.getVirtualHostsList()) {
StructOrError<VirtualHost> virtualHost =
parseVirtualHost(virtualHostProto, filterRegistry, pluginConfigMap,
optionalPlugins.build(), args);
if (virtualHost.getErrorDetail() != null) {
throw new ResourceInvalidException(
"RouteConfiguration contains invalid virtual host: " + virtualHost.getErrorDetail());
}
virtualHosts.add(virtualHost.getStruct());
}
return virtualHosts;
}
private static StructOrError<VirtualHost> parseVirtualHost(
io.envoyproxy.envoy.config.route.v3.VirtualHost proto, FilterRegistry filterRegistry,
Map<String, PluginConfig> pluginConfigMap,
Set<String> optionalPlugins, XdsResourceType.Args args) {
String name = proto.getName();
List<Route> routes = new ArrayList<>(proto.getRoutesCount());
for (io.envoyproxy.envoy.config.route.v3.Route routeProto : proto.getRoutesList()) {
StructOrError<Route> route = parseRoute(
routeProto, filterRegistry, pluginConfigMap, optionalPlugins, args);
if (route == null) {
continue;View on GitHub (pinned to 64daddc1f3)