{"record":{"id":"20e68e8ae1dbeca7","repo":"apache/druid","slug":"maxattempts-must-be-positive-limited-or-negative","errorCode":null,"errorMessage":"maxAttempts must be positive (limited) or negative (unlimited); cannot be zero.","messagePattern":"maxAttempts must be positive \\(limited\\) or negative \\(unlimited\\); cannot be zero\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/rpc/StandardRetryPolicy.java","lineNumber":75,"sourceCode":"  private final boolean retryNotAvailable;\n  private final boolean retryLoggable;\n\n  private StandardRetryPolicy(\n      long maxAttempts,\n      long minWaitMillis,\n      long maxWaitMillis,\n      boolean retryNotAvailable,\n      boolean retryLoggable\n  )\n  {\n    this.maxAttempts = maxAttempts;\n    this.minWaitMillis = minWaitMillis;\n    this.maxWaitMillis = maxWaitMillis;\n    this.retryNotAvailable = retryNotAvailable;\n    this.retryLoggable = retryLoggable;\n\n    if (maxAttempts == 0) {\n      throw new IAE(\"maxAttempts must be positive (limited) or negative (unlimited); cannot be zero.\");\n    }\n  }\n\n  public static Builder builder()\n  {\n    return new Builder();\n  }\n\n  /**\n   * Standard unlimited retry policy. Never stops retrying as long as errors remain retryable.\n   * See {@link ServiceClient} documentation for details on what errors are retryable.\n   */\n  public static StandardRetryPolicy unlimited()\n  {\n    return DEFAULT_UNLIMITED_POLICY;\n  }\n\n  /**","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/rpc/StandardRetryPolicy.java#L57-L93","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set maxAttempts to at least 1 for a finite number of attempts (1 = no retries).","Use a negative value such as -1 to express unlimited retries.","Clamp or default config-derived values: if maxAttempts == 0, use the builder default."],"exampleFix":"// before\nStandardRetryPolicy policy = StandardRetryPolicy.builder().maxAttempts(0).build();\n// after\nStandardRetryPolicy policy = StandardRetryPolicy.builder().maxAttempts(1).build(); // no retries","handlingStrategy":"validation","validationCode":"if (maxAttempts == 0) {\n  maxAttempts = 1; // no retries, one attempt\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat 0 as 'one attempt' in config mapping; use negative values for unlimited.","Document retry semantics where maxAttempts is configured."],"tags":["retry-policy","illegal-argument","config-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}