{"record":{"id":"238def3a464c02b6","repo":"grpc/grpc-java","slug":"outlier-detection-max-ejection-percent-is-100","errorCode":null,"errorMessage":"outlier_detection max_ejection_percent is > 100","messagePattern":"outlier_detection max_ejection_percent is > 100","errorType":"validation","errorClass":"ResourceInvalidException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/XdsClusterResource.java","lineNumber":411,"sourceCode":"    }\n    if (outlierDetection.hasMaxEjectionTime()) {\n      if (!Durations.isValid(outlierDetection.getMaxEjectionTime())) {\n        throw new ResourceInvalidException(\n            \"outlier_detection max_ejection_time is not a valid Duration\");\n      }\n      if (hasNegativeValues(outlierDetection.getMaxEjectionTime())) {\n        throw new ResourceInvalidException(\n            \"outlier_detection max_ejection_time has a negative value\");\n      }\n    }\n    if (outlierDetection.hasMaxEjectionPercent()\n        && outlierDetection.getMaxEjectionPercent().getValue() > 100) {\n      throw new ResourceInvalidException(\n          \"outlier_detection max_ejection_percent is > 100\");\n    }\n    if (outlierDetection.hasEnforcingSuccessRate()\n        && outlierDetection.getEnforcingSuccessRate().getValue() > 100) {\n      throw new ResourceInvalidException(\n          \"outlier_detection enforcing_success_rate is > 100\");\n    }\n    if (outlierDetection.hasFailurePercentageThreshold()\n        && outlierDetection.getFailurePercentageThreshold().getValue() > 100) {\n      throw new ResourceInvalidException(\n          \"outlier_detection failure_percentage_threshold is > 100\");\n    }\n    if (outlierDetection.hasEnforcingFailurePercentage()\n        && outlierDetection.getEnforcingFailurePercentage().getValue() > 100) {\n      throw new ResourceInvalidException(\n          \"outlier_detection enforcing_failure_percentage is > 100\");\n    }\n\n    return outlierDetection;\n  }\n\n  static boolean hasNegativeValues(Duration duration) {\n    return duration.getSeconds() < 0 || duration.getNanos() < 0;","sourceCodeStart":393,"sourceCodeEnd":429,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/XdsClusterResource.java#L393-L429","documentation":"This ResourceInvalidException is thrown when outlier_detection.max_ejection_percent exceeds 100. The field caps the percentage of hosts in a cluster that can be ejected for outlier status, so any value above 100 is invalid. validateOutlierDetection enforces this range while parsing the Cluster in parseNonAggregateCluster.","triggerScenarios":"A Cluster resource contains outlier_detection.max_ejection_percent with a UInt32Value greater than 100 (e.g. 150). The check runs only when the field is present (hasMaxEjectionPercent()).","commonSituations":"Confusing the 0-100 percent scale with a 0-1 fraction and sending 1.0 as 1000; typos like 1000; control planes forwarding unvalidated user input from dashboards.","solutions":["Set max_ejection_percent to a value in [0, 100] (Envoy default is 10).","Multiply fraction-based values by 100 (0.5 -> 50) before emitting.","Add range validation in the control plane before publishing the Cluster.","If you truly need all hosts ejectable, set it to exactly 100, not more."],"exampleFix":"# before\noutlier_detection:\n  max_ejection_percent: 150\n# after\noutlier_detection:\n  max_ejection_percent: 100","handlingStrategy":"validation","validationCode":"boolean validMaxEjectionPercent(io.envoyproxy.envoy.type.v3.UInt32Value v) {\n  return v == null || v.getValue() <= 100;\n}","typeGuard":"boolean inPercentRange(long v) {\n  return v >= 0 && v <= 100;\n}","tryCatchPattern":"try {\n  cluster = parseCluster(raw);\n} catch (io.grpc.xds.ResourceInvalidException e) {\n  if (e.getMessage().contains(\"max_ejection_percent\")) {\n    log.error(\"max_ejection_percent > 100 in CDS resource; fix control plane\");\n  }\n  return null;\n}","preventionTips":["Clamp percent fields to 0..100 at config generation.","Distinguish percent (0-100) from fraction (0-1) and per-ten-thousand scales in your schema.","Validate user input from dashboards before mapping into Cluster protos.","Use named constants (MAX_EJECTION_PERCENT_DEFAULT = 10) instead of raw numbers."],"tags":["xds","grpc","config-validation","range-check"],"backgroundTag":"value-out-of-range","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}