{"record":{"id":"d6a67a5380a578b9","repo":"grpc/grpc-java","slug":"outlier-detection-max-ejection-time-has-a-negative","errorCode":null,"errorMessage":"outlier_detection max_ejection_time has a negative value","messagePattern":"outlier_detection max_ejection_time has a negative value","errorType":"validation","errorClass":"ResourceInvalidException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/XdsClusterResource.java","lineNumber":406,"sourceCode":"      }\n      if (hasNegativeValues(outlierDetection.getBaseEjectionTime())) {\n        throw new ResourceInvalidException(\n            \"outlier_detection base_ejection_time has a negative value\");\n      }\n    }\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","sourceCodeStart":388,"sourceCodeEnd":424,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/XdsClusterResource.java#L388-L424","documentation":"validateOutlierDetection throws this ResourceInvalidException when outlier_detection.max_ejection_time is a valid Duration but has negative seconds or nanos. Negative ejection-time bounds are nonsensical for outlier detection's exponential backoff, so gRPC rejects the Cluster resource outright.","triggerScenarios":"outlier_detection.max_ejection_time set to a negative duration (e.g. {seconds: -300} or negative nanos) in a Cluster resource processed by parseNonAggregateCluster.","commonSituations":"'Unset' sentinels like -1 leaking into generated configs; buggy duration arithmetic on the control plane; copy-pasted negative values from interval fields in hand-edited YAML.","solutions":["Set max_ejection_time to a positive duration, e.g. {seconds: 300}.","Remove the field to use the default (3x base_ejection_time in Envoy semantics).","Reject negative durations at config-generation time.","Ensure max_ejection_time >= base_ejection_time in your generator."],"exampleFix":"# before\noutlier_detection:\n  max_ejection_time: { seconds: -300 }\n# after\noutlier_detection:\n  max_ejection_time: { seconds: 300 }","handlingStrategy":"validation","validationCode":"boolean nonNegativeMaxEjectionTime(com.google.protobuf.Duration d) {\n  return com.google.protobuf.util.Durations.isValid(d)\n      && d.getSeconds() >= 0 && d.getNanos() >= 0;\n}","typeGuard":"boolean nonNegativeDuration(com.google.protobuf.Duration d) {\n  return d != null && d.getSeconds() >= 0 && d.getNanos() >= 0\n      && com.google.protobuf.util.Durations.isValid(d);\n}","tryCatchPattern":"try {\n  parseCluster(resource);\n} catch (io.grpc.xds.ResourceInvalidException e) {\n  if (e.getMessage().contains(\"negative\")) {\n    log.warn(\"control plane emitted negative duration: {}\", e.getMessage());\n  }\n}","preventionTips":["Never emit -1 as a duration sentinel; omit the field.","Clamp computed durations to >= 0 before serialization.","Review generators for subtraction-based duration math.","Validate all outlier_detection durations with one shared helper."],"tags":["xds","grpc","config-validation","duration"],"backgroundTag":"invalid-duration-format","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"}