grpc/grpc-java · error · ResourceInvalidException

Multiple ClusterSpecifierPlugins with the same name

Error message

Multiple ClusterSpecifierPlugins with the same name: ${pluginName}

What it means

A RouteConfiguration may declare multiple ClusterSpecifierPlugins, but each must have a unique extension name. While building the pluginConfigMap in extractVirtualHosts, a put() returning a previous value reveals a duplicate name, so the RouteConfiguration is rejected as invalid.

Solutions

  1. Rename one of the duplicate ClusterSpecifierPlugins so extension names are unique
  2. Remove the redundant duplicate plugin entry
  3. Fix the control plane/templating that merges plugin lists without deduplication

Example fix

// before
cluster_specifier_plugins: [
  {extension: {name: "pluginA", ...}},
  {extension: {name: "pluginA", ...}}
]
// after
cluster_specifier_plugins: [
  {extension: {name: "pluginA", ...}},
  {extension: {name: "pluginB", ...}}
]
Defensive patterns

Strategy: validation

Validate before calling

// Detect duplicate plugin names before applying
Set<String> seen = new HashSet<>();
for (ClusterSpecifierPlugin p : rc.getClusterSpecifierPluginsList()) {
  if (!seen.add(p.getExtension().getName())) {
    throw new IllegalArgumentException("Duplicate plugin: " + p.getExtension().getName());
  }
}

Try / catch

try {
  xdsClient.watchResource(ROUTE_CONFIGURATION, name, watcher);
} catch (ResourceInvalidException e) {
  log.error("RouteConfiguration invalid: {}", e.getMessage());
}

Prevention

When it happens

Trigger: processRouteConfiguration -> extractVirtualHosts iterating routeConfig.getClusterSpecifierPluginsList() where two plugins share the same extension().getName(); the earlier entry was accepted (non-null PluginConfig) and the duplicate overwrites it.

Common situations: Generated RouteConfiguration merging plugin lists from multiple sources; copy-pasted plugin blocks with the same name but different configs; templating tools concatenating plugin arrays.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/e39ac46f154824dd. Report an issue: GitHub.

Appendix: source

Thrown at xds/src/main/java/io/grpc/xds/XdsRouteConfigureResource.java:157

      RouteConfiguration routeConfig, FilterRegistry filterRegistry, XdsResourceType.Args args)
      throws ResourceInvalidException {
    return new RdsUpdate(extractVirtualHosts(routeConfig, filterRegistry, args));
  }

  static List<VirtualHost> extractVirtualHosts(
      RouteConfiguration routeConfig, FilterRegistry filterRegistry, XdsResourceType.Args args)
      throws ResourceInvalidException {
    Map<String, PluginConfig> pluginConfigMap = new HashMap<>();
    ImmutableSet.Builder<String> optionalPlugins = ImmutableSet.builder();

    if (enableRouteLookup) {
      List<ClusterSpecifierPlugin> plugins = routeConfig.getClusterSpecifierPluginsList();
      for (ClusterSpecifierPlugin plugin : plugins) {
        String pluginName = plugin.getExtension().getName();
        PluginConfig pluginConfig = parseClusterSpecifierPlugin(plugin);
        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());
      }

View on GitHub (pinned to 64daddc1f3)