apache/druid · error · IllegalArgumentException

maxConcurrentStagesPerWorker must be positive

Error message

maxConcurrentStagesPerWorker must be positive

What it means

maxConcurrentStages (maxConcurrentStagesPerWorker) must be at least 1; the constructor throws IAE when it is <= 0. A worker cannot run zero or negative concurrent stage slots, so this fails fast at kernel construction.

Source

Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/kernel/controller/ControllerQueryKernelConfig.java:70

      boolean pipeline,
      boolean durableStorage,
      boolean faultTolerance,
      MSQDestination destination,
      @Nullable String controllerHost,
      @Nullable List<String> workerIds,
      Map<String, Object> workerContextMap
  )
  {
    if (maxRetainedPartitionSketchBytes <= 0) {
      throw new IAE("maxRetainedPartitionSketchBytes must be positive");
    }

    if (pipeline && maxConcurrentStages < 2) {
      throw new IAE("maxConcurrentStagesPerWorker must be >= 2 when pipelining");
    }

    if (maxConcurrentStages <= 0) {
      throw new IAE("maxConcurrentStagesPerWorker must be positive");
    }

    if (pipeline && faultTolerance) {
      throw new IAE("Cannot pipeline with fault tolerance");
    }

    if (pipeline && durableStorage) {
      throw new IAE("Cannot pipeline with durable storage");
    }

    if (faultTolerance && !durableStorage) {
      throw new IAE("Cannot have fault tolerance without durable storage");
    }

    this.maxRetainedPartitionSketchBytes = maxRetainedPartitionSketchBytes;
    this.maxConcurrentStages = maxConcurrentStages;
    this.pipeline = pipeline;
    this.durableStorage = durableStorage;

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Set maxConcurrentStages to a positive integer in the query context (default 2).
  2. If limiting concurrency, use 1 (with pipelining off) rather than 0.
  3. Guard programmatic construction: if (maxConcurrentStages <= 0) maxConcurrentStages = 2;

Example fix

// before
context.put("maxConcurrentStagesPerWorker", 0);

// after
context.put("maxConcurrentStagesPerWorker", 2);
Defensive patterns

Strategy: validation

Validate before calling

if (maxConcurrentStages <= 0) {
  throw new IllegalArgumentException("maxConcurrentStagesPerWorker must be >= 1");
}

Try / catch

try {
  kernelConfig = buildKernelConfig(ctx);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("must be positive")) {
    ctx.put("maxConcurrentStagesPerWorker", 2);
    kernelConfig = buildKernelConfig(ctx);
  }
}

Prevention

When it happens

Trigger: Constructing ControllerQueryKernelConfig with maxConcurrentStages = 0 or negative, e.g. an unparsed/empty context value coerced to 0, or an invalid user-supplied value for maxConcurrentStagesPerWorker.

Common situations: Query context value 'maxConcurrentStagesPerWorker' set to 0 or a negative number by mistake; numeric parsing of empty string; bad tuning guidance copied into cluster defaults.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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