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
- Regenerate the plugin config so its Any contains a valid (Typed)Struct payload
- Check that the type_url inside the Any matches envoy.config.core.v3.TypedStruct or the UDPA variant
- 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
- Serialize plugin configs as Struct/TypedStruct, never raw bytes
- Keep the producing and consuming Envoy API versions aligned
- Round-trip plugin configs through a parser in CI to catch malformed payloads
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
- Could not parse HttpConnectionManager config from…
- Failed to parse metadata key
- Invalid message type: " + unpackedMessage.getClass()
- Invalid message type
- Invalid message type
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)