apache/druid · error · IllegalStateException

Missing type for column [%s]

Error message

Missing type for column [%s]

What it means

StageDefinition's constructor verifies that every column in the stage's RowSignature has a known type. If signature.getColumnType(columnName) is empty for any column, the stage is malformed and ISE("Missing type for column [%s]") is thrown. Downstream frame serialization and shuffle logic require fully-typed signatures.

Source

Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/kernel/StageDefinition.java:143

      this.broadcastInputNumbers = (IntSet) broadcastInputNumbers;
    } else {
      this.broadcastInputNumbers = new IntAVLTreeSet(broadcastInputNumbers);
    }

    this.processor = Preconditions.checkNotNull(processor, "processor");
    this.signature = Preconditions.checkNotNull(signature, "signature");
    this.shuffleSpec = shuffleSpec;
    this.maxWorkerCount = maxWorkerCount;
    this.shuffleCheckHasMultipleValues = shuffleCheckHasMultipleValues;
    this.frameReader = Suppliers.memoize(() -> FrameReader.create(signature))::get;

    if (mustGatherResultKeyStatistics() && shuffleSpec.clusterBy().isEmpty()) {
      throw new IAE("Cannot shuffle with spec [%s] and nil clusterBy", shuffleSpec);
    }

    for (final String columnName : signature.getColumnNames()) {
      if (!signature.getColumnType(columnName).isPresent()) {
        throw new ISE("Missing type for column [%s]", columnName);
      }
    }

    for (final int broadcastInputNumber : this.broadcastInputNumbers) {
      if (broadcastInputNumber < 0 || broadcastInputNumber >= inputSpecs.size()) {
        throw new ISE("Broadcast input number out of range [%s]", broadcastInputNumber);
      }
    }
  }

  public static boolean mustGatherResultKeyStatistics(@Nullable final ShuffleSpec shuffleSpec)
  {
    return shuffleSpec != null
           && shuffleSpec.kind() == ShuffleKind.GLOBAL_SORT
           && ((GlobalSortShuffleSpec) shuffleSpec).mustGatherResultKeyStatistics();
  }

  public static StageDefinitionBuilder builder(final int stageNumber)

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Declare the type when building the signature: signatureBuilder.add(columnName, ColumnType.LONG) (or the correct type)
  2. Trace where the named column enters the signature and add its SqlType/ColumnType at that point
  3. Check custom extension processors that produce output columns and ensure they extend the signature with types

Example fix

// before
RowSignature.Builder.add("cnt");
// after
RowSignature.Builder.add("cnt", ColumnType.LONG);
Defensive patterns

Strategy: validation

Validate before calling

for (String col : signature.getColumnNames()) {
  if (!signature.getColumnType(col).isPresent()) {
    throw new IllegalStateException("Column missing type: " + col);
  }
}

Try / catch

try { StageDefinition def = builder.build(); } catch (IllegalStateException e) { log.error("Untyped column in signature: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Creating a StageDefinition whose RowSignature contains a column added without a type (e.g. via RowSignature.Builder.add(col) without the type argument, or a column synthesized by a processor that never set its type).

Common situations: Custom aggregation/processor extensions appending output columns without declaring their SqlType, or building signatures dynamically from input data where a column's type could not be inferred.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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