{"record":{"id":"c2e6799be39ec6c5","repo":"grpc/grpc-java","slug":"clusterloadassignment-has-sparse-priorities","errorCode":null,"errorMessage":"ClusterLoadAssignment has sparse priorities","messagePattern":"ClusterLoadAssignment has sparse priorities","errorType":"validation","errorClass":"ResourceInvalidException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/XdsEndpointResource.java","lineNumber":149,"sourceCode":"\n      LocalityLbEndpoints localityLbEndpoints = structOrError.getStruct();\n      int priority = localityLbEndpoints.priority();\n      maxPriority = Math.max(maxPriority, priority);\n      // Note endpoints with health status other than HEALTHY and UNKNOWN are still\n      // handed over to watching parties. It is watching parties' responsibility to\n      // filter out unhealthy endpoints. See EnvoyProtoData.LbEndpoint#isHealthy().\n      Locality locality =  parseLocality(localityLbEndpointsProto.getLocality());\n      localityLbEndpointsMap.put(locality, localityLbEndpoints);\n      if (!priorities.containsKey(priority)) {\n        priorities.put(priority, new HashSet<>());\n      }\n      if (!priorities.get(priority).add(locality)) {\n        throw new ResourceInvalidException(\"ClusterLoadAssignment has duplicate locality:\"\n            + locality + \" for priority:\" + priority);\n      }\n    }\n    if (priorities.size() != maxPriority + 1) {\n      throw new ResourceInvalidException(\"ClusterLoadAssignment has sparse priorities\");\n    }\n\n    for (ClusterLoadAssignment.Policy.DropOverload dropOverloadProto\n        : assignment.getPolicy().getDropOverloadsList()) {\n      dropOverloads.add(parseDropOverload(dropOverloadProto));\n    }\n    return new EdsUpdate(assignment.getClusterName(), localityLbEndpointsMap, dropOverloads);\n  }\n\n  private static Locality parseLocality(io.envoyproxy.envoy.config.core.v3.Locality proto) {\n    return Locality.create(proto.getRegion(), proto.getZone(), proto.getSubZone());\n  }\n\n  private static DropOverload parseDropOverload(\n      io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment.Policy.DropOverload proto) {\n    return DropOverload.create(proto.getCategory(), getRatePerMillion(proto.getDropPercentage()));\n  }\n","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/XdsEndpointResource.java#L131-L167","documentation":"Priorities in a ClusterLoadAssignment must be dense: every integer from 0 through the maximum observed priority must have at least one locality. If a priority level is skipped (e.g. priorities 0 and 2 exist but 1 does not), the EDS resource is rejected with ResourceInvalidException.","triggerScenarios":"processClusterLoadAssignment counts distinct priority keys in the priorities map and compares with maxPriority + 1; any gap (sparse set) triggers the error — e.g. localities declared at priority 0 and 2 with none at 1.","commonSituations":"Control planes removing all localities at a middle priority during failover reconfigurations; generators computing priorities non-contiguously; stale cached endpoint sets partially merged.","solutions":["Renumber priorities to be contiguous starting at 0 in the EDS response","Ensure the management server emits at least one locality for every priority 0..max","Fall back to lower priority count if a tier has no healthy endpoints instead of omitting it","Add control-plane validation for priority density before publishing updates"],"exampleFix":"# before\nlocalities:\n- {locality: A, priority: 0}\n- {locality: B, priority: 2}\n# after\nlocalities:\n- {locality: A, priority: 0}\n- {locality: B, priority: 1}","handlingStrategy":"validation","validationCode":"Set<Integer> prios = assignment.getEndpointsList().stream()\n    .map(LocalityLbEndpoints::getPriority)\n    .collect(Collectors.toSet());\nint max = Collections.max(prios);\nif (prios.size() != max + 1) throw new IllegalArgumentException(\"sparse priorities\");","typeGuard":null,"tryCatchPattern":"try {\n  update = XdsEndpointResource.getInstance().parse(args, resource);\n} catch (ResourceInvalidException e) {\n  logger.warn(\"EDS rejected: {}\", e.getMessage());\n}","preventionTips":["Renumber priorities contiguously from 0 in the control plane","Never skip a priority tier when reconfiguring failover","Validate priority density before publishing EDS updates"],"tags":["xds","eds","priority","grpc-java","failover"],"backgroundTag":"invalid-argument-value","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"}