apache/druid · error · RuntimeException
taskCountMin <= taskCountStart <= taskCountMax
Error message
taskCountMin <= taskCountStart <= taskCountMax
What it means
LagBasedAutoScalerConfig also validates an optional taskCountStart: when autoscaling is enabled and taskCountStart is provided, it must lie within the [taskCountMin, taskCountMax] range. A start value outside this window makes the initial task count unreachable by the scaler, so the config throws this RuntimeException.
Source
Thrown at indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/LagBasedAutoScalerConfig.java:103
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"
);
Preconditions.checkArgument(View on GitHub (pinned to 9b90983fd2)
Solutions
- Adjust taskCountStart so taskCountMin <= taskCountStart <= taskCountMax
- Remove taskCountStart (it is optional; taskCountMin is then used as the starting count)
- Widen taskCountMin/taskCountMax to include the desired start value
Example fix
// before "taskCountMin": 1, "taskCountMax": 3, "taskCountStart": 6 // after "taskCountMin": 1, "taskCountMax": 6, "taskCountStart": 6
Defensive patterns
Strategy: validation
Validate before calling
if (cfg.taskCountStart != null) {
if (cfg.taskCountStart > cfg.taskCountMax || cfg.taskCountStart < cfg.taskCountMin) throw new IllegalArgumentException("taskCountMin <= taskCountStart <= taskCountMax required");
} Try / catch
try { supervisor.start(spec); } catch (RuntimeException e) { if (e.getMessage().equals("taskCountMin <= taskCountStart <= taskCountMax")) { resubmitWithClampedStart(); } else { throw e; } } Prevention
- Clamp taskCountStart into [min,max] programmatically before submit
- Drop taskCountStart when unsure — it's optional
- Re-check start after changing min/max
When it happens
Trigger: Constructing LagBasedAutoScalerConfig with enableTaskAutoScaler=true, a non-null taskCountStart, and either taskCountStart > taskCountMax or taskCountStart < taskCountMin.
Common situations: Setting taskCountStart to the current task count after manually scaling, then changing min/max; typos in start; reusing a start value across supervisors with different ranges.
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
- taskCountMax or taskCountMin can't be null!
- taskCountMax can't lower than taskCountMin!
- Cannot simultaneously replace and append to existing segment
- Interval[%s] is empty, must specify a nonempty interval
- SeekableStreamSupervisorIOConfig does not support both prope
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/1d705cdfa198aa97.
Report an issue: GitHub.