grpc/grpc-java · error · ResourceInvalidException

ClusterSpecifierPlugin

Error message

ClusterSpecifierPlugin [${pluginName}] contains invalid proto

What it means

The ClusterSpecifierPlugin's typed config Any could not be unpacked into a TypedStruct (neither the UDPA nor the standard type_url variant), indicating corrupt or non-Struct proto payload. Parsing aborts with ResourceInvalidException wrapping the InvalidProtocolBufferException.

Solutions

  1. Regenerate the plugin config so its Any contains a valid (Typed)Struct payload
  2. Check that the type_url inside the Any matches envoy.config.core.v3.TypedStruct or the UDPA variant
  3. Update the control plane / gRPC xDS versions so serialization and parsing agree

Example fix

// before
Any any = Any.pack(BytesValue.of(...)); // not a Struct
// after
Any any = Any.pack(TypedStruct.newBuilder()
    .setTypeUrl("type.googleapis.com/xds.type.matcher.v3.Matcher")
    .setValue(structValue)
    .build());
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the plugin Any is a valid TypedStruct before use
try {
  Any.unpackCompatibleType(any, TypedStruct.class,
      "type.googleapis.com/xds.type.TypedStructUdpa",
      "type.googleapis.com/xds.type.TypedStruct");
} catch (InvalidProtocolBufferException e) {
  throw new IllegalArgumentException("Malformed plugin config", e);
}

Type guard

static boolean isTypedStruct(Any any) {
  return any.getTypeUrl().contains("TypedStruct");
}

Try / catch

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

Prevention

When it happens

Trigger: parseClusterSpecifierPlugin calling unpackCompatibleType(anyConfig, TypedStruct.class, TYPE_URL_TYPED_STRUCT_UDPA, TYPE_URL_TYPED_STRUCT) which throws InvalidProtocolBufferException for the plugin identified by pluginName.

Common situations: Control plane serializing malformed Struct values into the plugin's Any; binary payloads where a Struct/TypedStruct is expected; version skew where the producer uses an old UDPA type_url the parser can't decode.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


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

Appendix: source

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

  @Nullable // null if the plugin is not supported, but it's marked as optional.
  @VisibleForTesting
  static PluginConfig parseClusterSpecifierPlugin(
      ClusterSpecifierPlugin pluginProto, ClusterSpecifierPluginRegistry registry)
      throws ResourceInvalidException {
    TypedExtensionConfig extension = pluginProto.getExtension();
    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 {

View on GitHub (pinned to 64daddc1f3)