grpc/grpc-java · error · ResourceInvalidException

outlier_detection interval is not a valid Duration

Error message

outlier_detection interval is not a valid Duration

What it means

validateOutlierDetection checks the outlier_detection settings of a Cluster. If the interval duration proto is present but not a valid Duration (e.g. out-of-range seconds/nanos per Duration validation rules), the whole CDS resource is rejected with ResourceInvalidException.

Solutions

  1. Set a valid, normalized non-negative Duration for outlier_detection.interval on the management server
  2. Ensure seconds and nanos fields are within protobuf Duration bounds and correctly normalized
  3. Validate durations server-side before emitting CDS resources

Example fix

// before
Durations.newBuilder().setSeconds(999999999999L).setNanos(0).build() // interval
// after
com.google.protobuf.Duration.newBuilder().setSeconds(10).setNanos(0).build() // interval = 10s
Defensive patterns

Strategy: validation

Validate before calling

io.envoyproxy.envoy.config.cluster.v3.OutlierDetection od = cluster.getOutlierDetection();
if (od.hasInterval() && !Durations.isValid(od.getInterval())) {
  throw new IllegalArgumentException("interval is not a valid Duration");
}
if (od.hasInterval() && od.getInterval().getSeconds() < 0) {
  throw new IllegalArgumentException("interval is negative");
}

Try / catch

try {
  cdsUpdate = xdsClusterResource.parseResource(args);
} catch (ResourceInvalidException e) {
  if (e.getMessage().contains("not a valid Duration")) {
    logger.atWarning().log("Rejecting cluster outlier_detection: %s", e.getMessage());
  }
}

Prevention

When it happens

Trigger: A Cluster's outlier_detection.interval is set with values outside Duration limits — negative seconds with wrong nanos normalization, or seconds/nanos exceeding protobuf Duration bounds — such that Durations.isValid returns false.

Common situations: Control-plane bugs producing unnormalized Duration protos; hand-built cluster protos in tests with seconds > 315,576,000,000 or malformed nanos; config converters translating durations incorrectly.

Understand the failure class

Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/d3ee405a890bb669. Report an issue: GitHub.

Appendix: source

Thrown at xds/src/main/java/io/grpc/xds/XdsClusterResource.java:378

            "Cluster " + clusterName
                + ": LOGICAL DNS clusters socket_address must have port_value");
      }
      String dnsHostName = String.format(
          Locale.US, "%s:%d", socketAddress.getAddress(), socketAddress.getPortValue());
      return StructOrError.fromStruct(CdsUpdate.forLogicalDns(
          clusterName, dnsHostName, lrsServerInfo, maxConcurrentRequests,
          upstreamTlsContext, isHttp11ProxyAvailable, backendMetricPropagation));
    }
    return StructOrError.fromError(
        "Cluster " + clusterName + ": unsupported built-in discovery type: " + type);
  }

  static io.envoyproxy.envoy.config.cluster.v3.OutlierDetection validateOutlierDetection(
      io.envoyproxy.envoy.config.cluster.v3.OutlierDetection outlierDetection)
      throws ResourceInvalidException {
    if (outlierDetection.hasInterval()) {
      if (!Durations.isValid(outlierDetection.getInterval())) {
        throw new ResourceInvalidException("outlier_detection interval is not a valid Duration");
      }
      if (hasNegativeValues(outlierDetection.getInterval())) {
        throw new ResourceInvalidException("outlier_detection interval has a negative value");
      }
    }
    if (outlierDetection.hasBaseEjectionTime()) {
      if (!Durations.isValid(outlierDetection.getBaseEjectionTime())) {
        throw new ResourceInvalidException(
            "outlier_detection base_ejection_time is not a valid Duration");
      }
      if (hasNegativeValues(outlierDetection.getBaseEjectionTime())) {
        throw new ResourceInvalidException(
            "outlier_detection base_ejection_time has a negative value");
      }
    }
    if (outlierDetection.hasMaxEjectionTime()) {
      if (!Durations.isValid(outlierDetection.getMaxEjectionTime())) {
        throw new ResourceInvalidException(

View on GitHub (pinned to 64daddc1f3)