grpc/grpc-java · error · ResourceInvalidException

Unsupported ClusterSpecifierPlugin type

Error message

Unsupported ClusterSpecifierPlugin type: ${typeUrl}

What it means

The ClusterSpecifierPlugin's type_url does not correspond to any plugin implementation registered in the ClusterSpecifierPlugin registry. Unless the plugin is marked is_optional, the RouteConfiguration is rejected because gRPC cannot interpret the plugin semantics.

Solutions

  1. Upgrade grpc-xds to a version that supports the plugin type referenced by the type_url
  2. Change the route config to use a supported cluster specifier (plain cluster or weighted clusters)
  3. If the plugin is genuinely optional, set is_optional: true on the ClusterSpecifierPlugin so it's skipped
  4. Fix a typo'd or wrong type_url in the plugin extension config

Example fix

// before
cluster_specifier_plugins: [{
  extension: {name: "p", typed_config: {"@type": "type.googleapis.com/unknown.Plugin"}}
}]
// after
cluster_specifier_plugins: [{
  extension: {name: "p", typed_config: {"@type": "type.googleapis.com/xds.type.matcher.v3.Matcher", ...}},
  is_optional: true
}]
Defensive patterns

Strategy: fallback

Validate before calling

// Check plugin type support before deploying the route config
String typeUrl = extractTypeUrl(plugin.getTypedConfig());
if (!SUPPORTED_PLUGIN_TYPE_URLS.contains(typeUrl) && !plugin.getIsOptional()) {
  throw new IllegalArgumentException("Unsupported plugin type: " + typeUrl);
}

Try / catch

try {
  xdsClient.watchResource(ROUTE_CONFIGURATION, name, watcher);
} catch (ResourceInvalidException e) {
  if (e.getMessage().startsWith("Unsupported ClusterSpecifierPlugin type")) {
    log.warn("Falling back to non-plugin routing");
  }
}

Prevention

When it happens

Trigger: parseClusterSpecifierPlugin: registry.get(typeUrl) returns null (unknown type_url in the plugin's extension config) and pluginProto.getIsOptional() is false.

Common situations: Configs authored for newer/other Envoy plugin types (e.g. matcher-based plugins not yet supported by the gRPC version); version skew between control plane and grpc-xds; typo'd type_url in hand-written configs.

Related errors


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

Appendix: source

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

    String pluginName = extension.getName();
    Any anyConfig = extension.getTypedConfig();
    String typeUrl = anyConfig.getTypeUrl();
    Message rawConfig = anyConfig;
    if (typeUrl.equals(TYPE_URL_TYPED_STRUCT_UDPA) || typeUrl.equals(TYPE_URL_TYPED_STRUCT)) {
      try {
        TypedStruct typedStruct = unpackCompatibleType(
            anyConfig, TypedStruct.class, TYPE_URL_TYPED_STRUCT_UDPA, TYPE_URL_TYPED_STRUCT);
        typeUrl = typedStruct.getTypeUrl();
        rawConfig = typedStruct.getValue();
      } catch (InvalidProtocolBufferException e) {
        throw new ResourceInvalidException(
            "ClusterSpecifierPlugin [" + pluginName + "] contains invalid proto", e);
      }
    }
    io.grpc.xds.ClusterSpecifierPlugin plugin = registry.get(typeUrl);
    if (plugin == null) {
      if (!pluginProto.getIsOptional()) {
        throw new ResourceInvalidException("Unsupported ClusterSpecifierPlugin type: " + typeUrl);
      }
      return null;
    }
    ConfigOrError<? extends PluginConfig> pluginConfigOrError = plugin.parsePlugin(rawConfig);
    if (pluginConfigOrError.errorDetail != null) {
      throw new ResourceInvalidException(pluginConfigOrError.errorDetail);
    }
    return pluginConfigOrError.config;
  }

  static final class RdsUpdate implements ResourceUpdate {
    // The list virtual hosts that make up the route table.
    final List<VirtualHost> virtualHosts;

    RdsUpdate(List<VirtualHost> virtualHosts) {
      this.virtualHosts = Collections.unmodifiableList(
          new ArrayList<>(checkNotNull(virtualHosts, "virtualHosts")));
    }

View on GitHub (pinned to 64daddc1f3)