apache/druid · error · IllegalStateException

Expected complex type with defined complexTypeName, but got

Error message

Expected complex type with defined complexTypeName, but got [%s]

What it means

ComplexFieldReader.createFromType builds a reader for a COMPLEX column, which requires a named complex type (complexTypeName) so the matching ComplexMetricSerde can be resolved. It throws ISE when the ColumnType is null, is not ValueType.COMPLEX, or is COMPLEX without a complexTypeName set.

Source

Thrown at processing/src/main/java/org/apache/druid/frame/field/ComplexFieldReader.java:66

 * Format:
 * <p>
 * - 1 byte: {@link ComplexFieldWriter#NULL_BYTE} or {@link ComplexFieldWriter#NOT_NULL_BYTE}
 * - 4 bytes: length of serialized complex value, little-endian int
 * - N bytes: serialized complex value
 */
public class ComplexFieldReader implements FieldReader
{
  private final ComplexMetricSerde serde;

  ComplexFieldReader(final ComplexMetricSerde serde)
  {
    this.serde = Preconditions.checkNotNull(serde, "serde");
  }

  public static ComplexFieldReader createFromType(final ColumnType columnType)
  {
    if (columnType == null || columnType.getType() != ValueType.COMPLEX || columnType.getComplexTypeName() == null) {
      throw new ISE("Expected complex type with defined complexTypeName, but got [%s]", columnType);
    }

    final ComplexMetricSerde serde = ComplexMetrics.getSerdeForType(columnType.getComplexTypeName());

    if (serde == null) {
      throw new ISE("No serde for complexTypeName[%s]", columnType.getComplexTypeName());
    }

    return new ComplexFieldReader(serde);
  }

  @Override
  public ColumnValueSelector<?> makeColumnValueSelector(Memory memory, ReadableFieldPointer fieldPointer)
  {
    return new Selector<>(memory, fieldPointer, serde);
  }

  @Override

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Inspect the ColumnType with ColumnType.toString()/getType() and confirm it is ValueType.COMPLEX with a non-null complexTypeName.
  2. Ensure the column descriptor/serialized payload actually records the complex type name (e.g. hyperUnique, quantilesDoublesSketch).
  3. Route non-complex columns to FieldReaders.create (which dispatches per type) instead of createFromType.
  4. If schema came from old data, re-ingest or upgrade segments so complex type names are present.

Example fix

// before
if (type.getType() == ValueType.COMPLEX) { reader = ComplexFieldReader.createFromType(type); }
// after
if (type != null && type.getType() == ValueType.COMPLEX && type.getComplexTypeName() != null) {
  reader = ComplexFieldReader.createFromType(type);
} else {
  reader = FieldReaders.create(columnName, type);
}
Defensive patterns

Strategy: type-guard

Validate before calling

boolean usable = columnType != null && columnType.getType() == ValueType.COMPLEX && columnType.getComplexTypeName() != null;

Type guard

boolean isNamedComplex(ColumnType t) { return t != null && t.getType() == ValueType.COMPLEX && t.getComplexTypeName() != null; }

Try / catch

try { reader = ComplexFieldReader.createFromType(t); } catch (IllegalStateException e) { reader = FieldReaders.create(name, t); }

Prevention

When it happens

Trigger: Calling ComplexFieldReader.createFromType(columnType) with a null ColumnType; with a non-complex type (LONG, DOUBLE, STRING, ARRAY); or with a COMPLEX type whose complexTypeName is null, as happens when a complex column is deserialized from a descriptor missing the type name.

Common situations: Segment/column schema serialized by a version that did not persist complexTypeName; manually constructed ColumnType instances passed instead of ones parsed from the segment metadata; wiring a complex column through generic field-reader code paths that assume all types.

Related errors


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