{"record":{"id":"b81551711d7d8964","repo":"grpc/grpc-go","slug":"outlierdetectionloadbalancingconfig-interval-s","errorCode":null,"errorMessage":"OutlierDetectionLoadBalancingConfig.interval = %s; must be >= 0","messagePattern":"OutlierDetectionLoadBalancingConfig\\.interval = (.+?); must be >= 0","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/balancer/outlierdetection/balancer.go","lineNumber":131,"sourceCode":"\t}\n\n\t// Note: in the xds flow, these validations will never fail. The xdsclient\n\t// performs the same validations as here on the xds Outlier Detection\n\t// resource before parsing resource into JSON which this function gets\n\t// called with. A50 defines two separate places for these validations to\n\t// take place, the xdsclient and this ParseConfig method. \"When parsing a\n\t// config from JSON, if any of these requirements is violated, that should\n\t// be treated as a parsing error.\" - A50\n\tswitch {\n\t// \"The google.protobuf.Duration fields interval, base_ejection_time, and\n\t// max_ejection_time must obey the restrictions in the\n\t// google.protobuf.Duration documentation and they must have non-negative\n\t// values.\" - A50\n\t// Approximately 290 years is the maximum time that time.Duration (int64)\n\t// can represent. The restrictions on the protobuf.Duration field are to be\n\t// within +-10000 years. Thus, just check for negative values.\n\tcase lbCfg.Interval < 0:\n\t\treturn nil, fmt.Errorf(\"OutlierDetectionLoadBalancingConfig.interval = %s; must be >= 0\", lbCfg.Interval)\n\tcase lbCfg.BaseEjectionTime < 0:\n\t\treturn nil, fmt.Errorf(\"OutlierDetectionLoadBalancingConfig.base_ejection_time = %s; must be >= 0\", lbCfg.BaseEjectionTime)\n\tcase lbCfg.MaxEjectionTime < 0:\n\t\treturn nil, fmt.Errorf(\"OutlierDetectionLoadBalancingConfig.max_ejection_time = %s; must be >= 0\", lbCfg.MaxEjectionTime)\n\n\t// \"The fields max_ejection_percent,\n\t// success_rate_ejection.enforcement_percentage,\n\t// failure_percentage_ejection.threshold, and\n\t// failure_percentage.enforcement_percentage must have values less than or\n\t// equal to 100.\" - A50\n\tcase lbCfg.MaxEjectionPercent > 100:\n\t\treturn nil, fmt.Errorf(\"OutlierDetectionLoadBalancingConfig.max_ejection_percent = %v; must be <= 100\", lbCfg.MaxEjectionPercent)\n\tcase lbCfg.SuccessRateEjection != nil && lbCfg.SuccessRateEjection.EnforcementPercentage > 100:\n\t\treturn nil, fmt.Errorf(\"OutlierDetectionLoadBalancingConfig.SuccessRateEjection.enforcement_percentage = %v; must be <= 100\", lbCfg.SuccessRateEjection.EnforcementPercentage)\n\tcase lbCfg.FailurePercentageEjection != nil && lbCfg.FailurePercentageEjection.Threshold > 100:\n\t\treturn nil, fmt.Errorf(\"OutlierDetectionLoadBalancingConfig.FailurePercentageEjection.threshold = %v; must be <= 100\", lbCfg.FailurePercentageEjection.Threshold)\n\tcase lbCfg.FailurePercentageEjection != nil && lbCfg.FailurePercentageEjection.EnforcementPercentage > 100:\n\t\treturn nil, fmt.Errorf(\"OutlierDetectionLoadBalancingConfig.FailurePercentageEjection.enforcement_percentage = %v; must be <= 100\", lbCfg.FailurePercentageEjection.EnforcementPercentage)","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/balancer/outlierdetection/balancer.go#L113-L149","documentation":"Returned by the outlier detection balancer's ParseConfig (internal/xds/balancer/outlierdetection/balancer.go:130) when the LBConfig `interval` field — a google.protobuf.Duration decoded into time.Duration — is negative. gRFC A50 mandates that interval, base_ejection_time, and max_ejection_time be non-negative, and the library enforces this in ParseConfig as defense-in-depth alongside the same check performed earlier in the xDS client. The default is 10s when the field is omitted.","triggerScenarios":"Calling ParseConfig on the `outlier_detection_experimental` balancer with JSON whose `interval` is a negative duration (e.g. \"-5s\") or a protobuf Duration with negative seconds. In the normal xDS path this is a redundant second check; directly it fires when a hand-built service config or a buggy resolver supplies interval < 0.","commonSituations":"A control-plane/EDS resource encodes a negative duration by mistake; manually constructing an outlier detection LBConfig for testing with a bad value; a protobuf Duration round-trip producing negative seconds.","solutions":["Set `interval` to a non-negative Duration (omit it to accept the 10s default)","Correct the upstream xDS/EDS resource that emitted the negative value","Validate the LBConfig JSON against A50 ranges before handing it to ParseConfig"],"exampleFix":"// before\n{\"interval\":\"-5s\",\"childPolicy\":[{\"round_robin\":{}}]}\n// after\n{\"interval\":\"10s\",\"childPolicy\":[{\"round_robin\":{}}]}","handlingStrategy":"validation","validationCode":"// Validate outlier-detection interval before ParseConfig.\nvar probe struct{ Interval string `json:\"interval\"` }\n_ = json.Unmarshal(raw, &probe)\nif probe.Interval != \"\" {\n    d, err := time.ParseDuration(probe.Interval)\n    if err != nil || d < 0 {\n        return fmt.Errorf(\"interval must be a non-negative duration, got %q\", probe.Interval)\n    }\n}","typeGuard":null,"tryCatchPattern":"lbCfg, err := outlierBB.ParseConfig(raw)\nif err != nil {\n    logger.Warningf(\"rejecting outlier detection config: %v\", err)\n    return fallbackLBConfig // keep prior valid config\n}","preventionTips":["Treat all outlier-detection durations as non-negative at the source","Omit optional duration fields to take documented defaults","Unit-test the config emitter against A50 ranges"],"tags":["grpc","xds","outlier-detection","load-balancing","config","go"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}