apache/druid · error · UOE

%s

Error message

%s

What it means

In IndexTask.determineShardSpecs, after handling HASH and LINEAR partitionsSpec types there is a final else branch that throws UOE for any other partitionsSpec implementation, formatting the class name into the message. It is the internal counterpart of the isReady() check, catching specs whose type changed or bypassed early validation.

Source

Thrown at indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java:623

    // Must determine intervals if unknown, since we acquire all locks before processing any data.
    final boolean determineIntervals = granularitySpec.inputIntervals().isEmpty();

    // Must determine partitions if rollup is guaranteed and the user didn't provide a specific value.
    final boolean determineNumPartitions = partitionsSpec.needsDeterminePartitions();

    // if we were given number of shards per interval and the intervals, we don't need to scan the data
    if (!determineNumPartitions && !determineIntervals) {
      log.info("Skipping determine partition scan");
      if (partitionsSpec.getType() == SecondaryPartitionType.HASH) {
        return PartialHashSegmentGenerateTask.createHashPartitionAnalysisFromPartitionsSpec(
            granularitySpec,
            (HashedPartitionsSpec) partitionsSpec,
            null // not overriding numShards
        );
      } else if (partitionsSpec.getType() == SecondaryPartitionType.LINEAR) {
        return createLinearPartitionAnalysis(granularitySpec, (DynamicPartitionsSpec) partitionsSpec);
      } else {
        throw new UOE("%s", partitionsSpec.getClass().getName());
      }
    } else {
      // determine intervals containing data and prime HLL collectors
      log.info("Determining intervals and shardSpecs");
      return createShardSpecsFromInput(
          jsonMapper,
          ingestionSchema,
          inputSource,
          tmpDir,
          granularitySpec,
          partitionsSpec,
          determineIntervals
      );
    }
  }

  private static boolean addDeterminePartitionStatsToReport(boolean isFullReport, IngestionState ingestionState)
  {

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Use partitionsSpec type "linear" or "hash" for batch index tasks
  2. If programmatic, ensure only HashedPartitionsSpec or DynamicPartitionsSpec are constructed for IndexTask
  3. Route range-partitioned ingestion to the compaction task or MSQ engine
  4. Validate the spec type with a pre-submit check on tuningConfig.partitionsSpec.type

Example fix

// before
final PartitionsSpec spec = new MaxSizePartitionsSpec(...); // unsupported type
// after
final PartitionsSpec spec = new HashedPartitionsSpec(null, 4, null, null, null);
Defensive patterns

Strategy: validation

Validate before calling

if (partitionsSpec.getType() != SecondaryPartitionType.HASH
    && partitionsSpec.getType() != SecondaryPartitionType.LINEAR) {
  throw new IllegalArgumentException("Unsupported for IndexTask: " + partitionsSpec.getType());
}

Type guard

boolean supported(PartitionsSpec p) {
  return p instanceof HashedPartitionsSpec || p instanceof DynamicPartitionsSpec;
}

Try / catch

try {
  task.determineShardSpecs(...);
} catch (UnsupportedOperationException e) {
  throw new IllegalStateException("Convert partitionsSpec to hash/linear before batch ingestion", e);
}

Prevention

When it happens

Trigger: A partitionsSpec whose getType() is neither HASH nor LINEAR reaching shard-spec determination — e.g., a custom partitionsSpec implementation, a spec constructed programmatically with a non-standard type, or a type added in a newer Druid version used against an older core.

Common situations: Range/single-dim partitioning specs submitted to IndexTask; plugin-provided partitionsSpec types not supported by the batch engine; spec deserialized from a config written for a different engine (compaction or MSQ).

Related errors


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