apache/druid · error · IllegalArgumentException
Can't use both maxNumSubTasks and maxNumConcurrentSubTasks.
Error message
Can't use both maxNumSubTasks and maxNumConcurrentSubTasks. Use maxNumConcurrentSubTasks instead
What it means
Thrown in the ParallelIndexTuningConfig constructor when both maxNumSubTasks and maxNumConcurrentSubTasks are set. They are aliases for the same concept; the modern field is maxNumConcurrentSubTasks, so specifying both is ambiguous and rejected with an IAE.
Source
Thrown at indexing-service/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/ParallelIndexTuningConfig.java:180
partitionsSpec,
indexSpec,
indexSpecForIntermediatePersists,
maxPendingPersists,
forceGuaranteedRollup,
reportParseExceptions,
null,
pushTimeout,
segmentWriteOutMediumFactory,
logParseExceptions,
maxParseExceptions,
maxSavedParseExceptions,
maxColumnsToMerge,
awaitSegmentAvailabilityTimeoutMillis,
numPersistThreads
);
if (maxNumSubTasks != null && maxNumConcurrentSubTasks != null) {
throw new IAE("Can't use both maxNumSubTasks and maxNumConcurrentSubTasks. Use maxNumConcurrentSubTasks instead");
}
this.splitHintSpec = splitHintSpec;
if (maxNumConcurrentSubTasks == null) {
this.maxNumConcurrentSubTasks = maxNumSubTasks == null ? DEFAULT_MAX_NUM_CONCURRENT_SUB_TASKS : maxNumSubTasks;
} else {
this.maxNumConcurrentSubTasks = maxNumConcurrentSubTasks;
}
this.maxRetry = maxRetry == null ? DEFAULT_MAX_RETRY : maxRetry;
this.taskStatusCheckPeriodMs = taskStatusCheckPeriodMs == null ?
DEFAULT_TASK_STATUS_CHECK_PERIOD_MS :
taskStatusCheckPeriodMs;
this.chatHandlerTimeout = chatHandlerTimeout == null ? DEFAULT_CHAT_HANDLER_TIMEOUT : chatHandlerTimeout;
this.chatHandlerNumRetries = chatHandlerNumRetries == null
? DEFAULT_CHAT_HANDLER_NUM_RETRIES
: chatHandlerNumRetries;View on GitHub (pinned to 9b90983fd2)
Solutions
- Remove maxNumSubTasks and keep only maxNumConcurrentSubTasks.
- If the config came from a template/defaults merger, deduplicate the keys before submission.
- If only maxNumSubTasks is present it is still accepted (treated as concurrency), so removing the newer key also works.
Example fix
// before
"tuningConfig": { "type": "parallel_index", "maxNumSubTasks": 4, "maxNumConcurrentSubTasks": 4 }
// after
"tuningConfig": { "type": "parallel_index", "maxNumConcurrentSubTasks": 4 } Defensive patterns
Strategy: validation
Validate before calling
const keys = Object.keys(tuningConfig);
if (keys.includes('maxNumSubTasks') && keys.includes('maxNumConcurrentSubTasks')) {
throw new Error('Set only maxNumConcurrentSubTasks; maxNumSubTasks is a legacy alias');
} Prevention
- Standardize on maxNumConcurrentSubTasks in all templates
- Strip legacy keys when upgrading task JSON across Druid versions
- Validate task JSON against the tuning-config schema before submission
When it happens
Trigger: Task JSON or programmatic tuning config containing both keys (e.g. an older spec upgraded by adding maxNumConcurrentSubTasks without removing maxNumSubTasks).
Common situations: Upgrading configs across Druid versions where the field was renamed; template-generated specs merging defaults that include both keys; copy-paste from different docs.
Related errors
- Partition count must be 1 when adjustable is true, but was [
- Cannot provide 'order' incompatible with 'orderBy'
- If a local input source sets property %s, it must also set p
- Aggregation [%s] does not support column [%s] of type [%s].
- Cannot accept both 'splitPoints' and 'numBins'
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/66bde550aa6a6564.
Report an issue: GitHub.