{"record":{"id":"f49bdafaae488297","repo":"grpc/grpc-java","slug":"failed-to-parse-locality-endpoint-metadata-e-ge","errorCode":null,"errorMessage":"Failed to parse Locality Endpoint metadata: ${e.getMessage()}","messagePattern":"Failed to parse Locality Endpoint metadata: (.+?)","errorType":"validation","errorClass":"ResourceInvalidException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/XdsEndpointResource.java","lineNumber":210,"sourceCode":"  @VisibleForTesting\n  @Nullable\n  static StructOrError<LocalityLbEndpoints> parseLocalityLbEndpoints(\n      io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints proto)\n      throws ResourceInvalidException {\n    // Filter out localities without or with 0 weight.\n    if (!proto.hasLoadBalancingWeight() || proto.getLoadBalancingWeight().getValue() < 1) {\n      return null;\n    }\n    if (proto.getPriority() < 0) {\n      return StructOrError.fromError(\"negative priority\");\n    }\n\n    ImmutableMap<String, Object> localityMetadata;\n    MetadataRegistry registry = MetadataRegistry.getInstance();\n    try {\n      localityMetadata = registry.parseMetadata(proto.getMetadata());\n    } catch (ResourceInvalidException e) {\n      throw new ResourceInvalidException(\"Failed to parse Locality Endpoint metadata: \"\n          + e.getMessage(), e);\n    }\n    List<Endpoints.LbEndpoint> endpoints = new ArrayList<>(proto.getLbEndpointsCount());\n    for (io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint endpoint : proto.getLbEndpointsList()) {\n      // The endpoint field of each lb_endpoints must be set.\n      // Inside of it: the address field must be set.\n      if (!endpoint.hasEndpoint() || !endpoint.getEndpoint().hasAddress()) {\n        return StructOrError.fromError(\"LbEndpoint with no endpoint/address\");\n      }\n      ImmutableMap<String, Object> endpointMetadata;\n      try {\n        endpointMetadata = registry.parseMetadata(endpoint.getMetadata());\n      } catch (ResourceInvalidException e) {\n        throw new ResourceInvalidException(\"Failed to parse Endpoint metadata: \"\n            + e.getMessage(), e);\n      }\n      List<java.net.SocketAddress> addresses = new ArrayList<>();\n      addresses.add(getInetSocketAddress(endpoint.getEndpoint().getAddress()));","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/XdsEndpointResource.java#L192-L228","documentation":"gRPC's xDS client parses the metadata Struct attached to each LocalityLbEndpoints in an EDS ClusterLoadAssignment resource via MetadataRegistry.parseMetadata. If that Struct cannot be parsed into valid typed metadata, the whole EDS resource is rejected with this ResourceInvalidException (wrapping the underlying reason) and the management server receives a NACK.","triggerScenarios":"An EDS ClusterLoadAssignment resource is delivered whose lb_endpoints[i].locality.metadata Struct uses a type or shape MetadataRegistry cannot parse (e.g. wrong metadata key type, unsupported proto Struct encoding, or a registered parser rejects a field). parseLocalityLbEndpoints catches the ResourceInvalidException from parseMetadata and rethrows with this prefix.","commonSituations":"Control plane (Envoy-based xDS server, Istio, custom management server) emits locality metadata that violates the gRPC metadata convention; a hand-written EDS response contains malformed metadata; a MetadataRegistry custom parser is stricter than what the control plane emits.","solutions":["Inspect the nested cause message (e.getMessage() appended) to find which metadata field failed parsing in MetadataRegistry","Fix the metadata Struct in the EDS resource on the xDS management server so it conforms to the gRPC xDS metadata schema","Ensure the locality metadata uses supported JSON Struct types (string/number/bool/nested structs) with expected keys","Re-push the corrected ClusterLoadAssignment resource so the client ACKs instead of NACKs"],"exampleFix":"// before (control plane emits unsupported metadata)\nmetadata { fields { key: \"zone\" value { list_value { values { struct_value {} } } } } }\n// after\nmetadata { fields { key: \"zone\" value { string_value: \"us-east-1a\" } } }","handlingStrategy":"validation","validationCode":"// Control-plane side: validate locality metadata before sending\nMetadataRegistry registry = MetadataRegistry.getInstance();\ntry {\n  registry.parseMetadata(localityProto.getMetadata()); // must not throw\n} catch (ResourceInvalidException e) {\n  throw new IllegalStateException(\"Invalid locality metadata: \" + e.getMessage(), e);\n}","typeGuard":"boolean isValidLocalityMetadata(com.google.protobuf.Struct metadata) {\n  try {\n    MetadataRegistry.getInstance().parseMetadata(metadata);\n    return true;\n  } catch (ResourceInvalidException e) {\n    return false;\n  }\n}","tryCatchPattern":"// Client side: observe NACK reasons via xDS client listeners\nxdsClient.addResourceWatcher(listener, new Watcher<EdsUpdate>() {\n  @Override public void onError(Status error) {\n    logger.log(WARNING, \"EDS resource rejected: \" + error.getDescription()); // contains 'Failed to parse Locality Endpoint metadata: ...'\n  }\n  @Override public void onResourceDoesNotExist(String resourceName) {}\n  @Override public void onChanged(EdsUpdate update) {}\n});","preventionTips":["Validate metadata Structs against MetadataRegistry on the control plane before publishing","Use only supported JSON Struct value types in xDS metadata","Keep control-plane metadata schemas in sync with gRPC-Java's MetadataRegistry versions","Watch xDS NACK (error) callbacks in logs to catch bad resources at push time"],"tags":["grpc","xds","metadata","eds","control-plane"],"backgroundTag":"schema-validation-failed","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}