apache/druid · error · RuntimeException

taskCountMax can't lower than taskCountMin!

Error message

taskCountMax can't lower than taskCountMin!

What it means

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.

Source

Thrown at indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/LagBasedAutoScalerConfig.java:101

  {
    this.enableTaskAutoScaler = enableTaskAutoScaler != null ? enableTaskAutoScaler : false;
    this.lagCollectionIntervalMillis = lagCollectionIntervalMillis != null ? lagCollectionIntervalMillis : 30000;
    this.lagCollectionRangeMillis = lagCollectionRangeMillis != null ? lagCollectionRangeMillis : 600000;
    this.scaleActionStartDelayMillis = scaleActionStartDelayMillis != null ? scaleActionStartDelayMillis : 300000;
    this.scaleActionPeriodMillis = scaleActionPeriodMillis != null ? scaleActionPeriodMillis : 60000;
    this.scaleOutThreshold = scaleOutThreshold != null ? scaleOutThreshold : 6000000;
    this.scaleInThreshold = scaleInThreshold != null ? scaleInThreshold : 1000000;
    this.triggerScaleOutFractionThreshold = triggerScaleOutFractionThreshold != null ? triggerScaleOutFractionThreshold : 0.3;
    this.triggerScaleInFractionThreshold = triggerScaleInFractionThreshold != null ? triggerScaleInFractionThreshold : 0.9;
    this.lagAggregate = lagAggregate;

    // 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.
    // If autoscaler is disabled, no matter what configs are set, they are not used.
    if (this.enableTaskAutoScaler) {
      if (taskCountMax == null || taskCountMin == null) {
        throw new RuntimeException("taskCountMax or taskCountMin can't be null!");
      } else if (taskCountMax < taskCountMin) {
        throw new RuntimeException("taskCountMax can't lower than taskCountMin!");
      } else if (taskCountStart != null && (taskCountStart > taskCountMax || taskCountStart < taskCountMin)) {
        throw new RuntimeException("taskCountMin <= taskCountStart <= taskCountMax");
      }
      this.taskCountMax = taskCountMax;
      this.taskCountMin = taskCountMin;
      this.taskCountStart = taskCountStart;
    }

    this.scaleInStep = scaleInStep != null ? scaleInStep : 1;
    this.scaleOutStep = scaleOutStep != null ? scaleOutStep : 2;
    this.minTriggerScaleActionFrequencyMillis =
        minTriggerScaleActionFrequencyMillis != null ? minTriggerScaleActionFrequencyMillis : 600000;
    this.minScaleUpDelay = Configs.valueOrDefault(minScaleUpDelay, Duration.millis(this.minTriggerScaleActionFrequencyMillis));
    this.minScaleDownDelay = Configs.valueOrDefault(minScaleDownDelay, Duration.millis(this.minTriggerScaleActionFrequencyMillis));

    Preconditions.checkArgument(
        stopTaskCountRatio == null || (stopTaskCountRatio > 0.0 && stopTaskCountRatio <= 1.0),
        "0.0 < stopTaskCountRatio <= 1.0"

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Swap or correct taskCountMax and taskCountMin so taskCountMax >= taskCountMin in the supervisor spec
  2. Validate the bounds in your deployment tooling before submitting the supervisor spec
  3. If you don't want scaling, set enableTaskAutoScaler=false or leave the autoscaler block empty {}

Example fix

// before
"taskCountMin": 4,
"taskCountMax": 2
// after
"taskCountMin": 2,
"taskCountMax": 4
Defensive patterns

Strategy: validation

Validate before calling

if (cfg.enableTaskAutoScaler) {
  if (cfg.taskCountMax == null || cfg.taskCountMin == null) throw new IllegalArgumentException("taskCountMax/taskCountMin required");
  if (cfg.taskCountMax < cfg.taskCountMin) throw new IllegalArgumentException("taskCountMax must be >= taskCountMin");
}

Try / catch

try { supervisor.start(spec); } catch (RuntimeException e) { if (e.getMessage().contains("taskCountMax")) { fixBoundsAndResubmit(); } else { throw e; } }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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