apache/druid · error · IllegalArgumentException

maxAttempts must be positive (limited) or negative…

Error message

maxAttempts must be positive (limited) or negative (unlimited); cannot be zero.

What it means

StandardRetryPolicy encodes limited retries as a positive maxAttempts and unlimited retries as a negative value. Zero is semantically meaningless (would mean never try at all), so the constructor rejects it with IllegalArgumentException.

Solutions

  1. Set maxAttempts to at least 1 for a finite number of attempts (1 = no retries).
  2. Use a negative value such as -1 to express unlimited retries.
  3. Clamp or default config-derived values: if maxAttempts == 0, use the builder default.

Example fix

// before
StandardRetryPolicy policy = StandardRetryPolicy.builder().maxAttempts(0).build();
// after
StandardRetryPolicy policy = StandardRetryPolicy.builder().maxAttempts(1).build(); // no retries
Defensive patterns

Strategy: validation

Validate before calling

if (maxAttempts == 0) {
  maxAttempts = 1; // no retries, one attempt
}

Prevention

When it happens

Trigger: Building a StandardRetryPolicy via the constructor or Builder with maxAttempts = 0, typically from a config value that defaulted to 0 or was computed by subtraction.

Common situations: Users setting "maxAttempts: 0" in config expecting retries to be disabled (should use negative for unlimited or 1 for no retries), or code computing maxRetries - 1 yielding 0.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/20e68e8ae1dbeca7. Report an issue: GitHub.

Appendix: source

Thrown at server/src/main/java/org/apache/druid/rpc/StandardRetryPolicy.java:75

  private final boolean retryNotAvailable;
  private final boolean retryLoggable;

  private StandardRetryPolicy(
      long maxAttempts,
      long minWaitMillis,
      long maxWaitMillis,
      boolean retryNotAvailable,
      boolean retryLoggable
  )
  {
    this.maxAttempts = maxAttempts;
    this.minWaitMillis = minWaitMillis;
    this.maxWaitMillis = maxWaitMillis;
    this.retryNotAvailable = retryNotAvailable;
    this.retryLoggable = retryLoggable;

    if (maxAttempts == 0) {
      throw new IAE("maxAttempts must be positive (limited) or negative (unlimited); cannot be zero.");
    }
  }

  public static Builder builder()
  {
    return new Builder();
  }

  /**
   * Standard unlimited retry policy. Never stops retrying as long as errors remain retryable.
   * See {@link ServiceClient} documentation for details on what errors are retryable.
   */
  public static StandardRetryPolicy unlimited()
  {
    return DEFAULT_UNLIMITED_POLICY;
  }

  /**

View on GitHub (pinned to 9b90983fd2)