{"record":{"id":"5b2d24668cf789ce","repo":"apache/druid","slug":"taskcountmax-can-t-lower-than-taskcountmin","errorCode":null,"errorMessage":"taskCountMax can't lower than taskCountMin!","messagePattern":"taskCountMax can't lower than taskCountMin!","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/LagBasedAutoScalerConfig.java","lineNumber":101,"sourceCode":"  {\n    this.enableTaskAutoScaler = enableTaskAutoScaler != null ? enableTaskAutoScaler : false;\n    this.lagCollectionIntervalMillis = lagCollectionIntervalMillis != null ? lagCollectionIntervalMillis : 30000;\n    this.lagCollectionRangeMillis = lagCollectionRangeMillis != null ? lagCollectionRangeMillis : 600000;\n    this.scaleActionStartDelayMillis = scaleActionStartDelayMillis != null ? scaleActionStartDelayMillis : 300000;\n    this.scaleActionPeriodMillis = scaleActionPeriodMillis != null ? scaleActionPeriodMillis : 60000;\n    this.scaleOutThreshold = scaleOutThreshold != null ? scaleOutThreshold : 6000000;\n    this.scaleInThreshold = scaleInThreshold != null ? scaleInThreshold : 1000000;\n    this.triggerScaleOutFractionThreshold = triggerScaleOutFractionThreshold != null ? triggerScaleOutFractionThreshold : 0.3;\n    this.triggerScaleInFractionThreshold = triggerScaleInFractionThreshold != null ? triggerScaleInFractionThreshold : 0.9;\n    this.lagAggregate = lagAggregate;\n\n    // Only do taskCountMax and taskCountMin check when autoscaler is enabled. So that users left autoConfig empty{} will not throw any exception and autoscaler is disabled.\n    // If autoscaler is disabled, no matter what configs are set, they are not used.\n    if (this.enableTaskAutoScaler) {\n      if (taskCountMax == null || taskCountMin == null) {\n        throw new RuntimeException(\"taskCountMax or taskCountMin can't be null!\");\n      } else if (taskCountMax < taskCountMin) {\n        throw new RuntimeException(\"taskCountMax can't lower than taskCountMin!\");\n      } else if (taskCountStart != null && (taskCountStart > taskCountMax || taskCountStart < taskCountMin)) {\n        throw new RuntimeException(\"taskCountMin <= taskCountStart <= taskCountMax\");\n      }\n      this.taskCountMax = taskCountMax;\n      this.taskCountMin = taskCountMin;\n      this.taskCountStart = taskCountStart;\n    }\n\n    this.scaleInStep = scaleInStep != null ? scaleInStep : 1;\n    this.scaleOutStep = scaleOutStep != null ? scaleOutStep : 2;\n    this.minTriggerScaleActionFrequencyMillis =\n        minTriggerScaleActionFrequencyMillis != null ? minTriggerScaleActionFrequencyMillis : 600000;\n    this.minScaleUpDelay = Configs.valueOrDefault(minScaleUpDelay, Duration.millis(this.minTriggerScaleActionFrequencyMillis));\n    this.minScaleDownDelay = Configs.valueOrDefault(minScaleDownDelay, Duration.millis(this.minTriggerScaleActionFrequencyMillis));\n\n    Preconditions.checkArgument(\n        stopTaskCountRatio == null || (stopTaskCountRatio > 0.0 && stopTaskCountRatio <= 1.0),\n        \"0.0 < stopTaskCountRatio <= 1.0\"","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/LagBasedAutoScalerConfig.java#L83-L119","documentation":"LagBasedAutoScalerConfig validates its task-count fields when the task autoscaler is enabled. If taskCountMax is set lower than taskCountMin, the configuration is rejected at construction time with this RuntimeException, since the scaler cannot scale between a range whose bounds are inverted. Validation only runs when enableTaskAutoScaler is true, so an empty {} config is accepted.","triggerScenarios":"Constructing a LagBasedAutoScalerConfig (or supplying supervisor autoscaler properties, e.g. Kafka/Kinesis taskCountMax/taskCountMin) with enableTaskAutoScaler=true and a taskCountMax value numerically less than taskCountMin.","commonSituations":"Hand-edited supervisor spec JSON where min/max are swapped or typos like max=2, min=4; programmatically generated configs computing max from a wrong variable; copying configs between supervisors without adjusting bounds.","solutions":["Swap or correct taskCountMax and taskCountMin so taskCountMax >= taskCountMin in the supervisor spec","Validate the bounds in your deployment tooling before submitting the supervisor spec","If you don't want scaling, set enableTaskAutoScaler=false or leave the autoscaler block empty {}"],"exampleFix":"// before\n\"taskCountMin\": 4,\n\"taskCountMax\": 2\n// after\n\"taskCountMin\": 2,\n\"taskCountMax\": 4","handlingStrategy":"validation","validationCode":"if (cfg.enableTaskAutoScaler) {\n  if (cfg.taskCountMax == null || cfg.taskCountMin == null) throw new IllegalArgumentException(\"taskCountMax/taskCountMin required\");\n  if (cfg.taskCountMax < cfg.taskCountMin) throw new IllegalArgumentException(\"taskCountMax must be >= taskCountMin\");\n}","typeGuard":null,"tryCatchPattern":"try { supervisor.start(spec); } catch (RuntimeException e) { if (e.getMessage().contains(\"taskCountMax\")) { fixBoundsAndResubmit(); } else { throw e; } }","preventionTips":["Always define taskCountMax >= taskCountMin in autoscaler specs","Validate supervisor JSON with a schema linter before submission","Centralize autoscaler defaults in one template"],"tags":["druid","config","autoscaler","validation"],"backgroundTag":"invalid-config-value","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}