apache/druid · error · IllegalArgumentException

[%s] column must have type [%s] or no type. Found [%s]

Error message

[%s] column must have type [%s] or no type. Found [%s]

What it means

ColumnSpec.validate enforces that the special __time column has Druid type LONG (or no explicit type). Assigning any other dataType (e.g. string, double) to the time column is invalid because the primary time column is internally stored as a long epoch millis value.

Source

Thrown at server/src/main/java/org/apache/druid/catalog/model/ColumnSpec.java:108

  {
    return dataType;
  }

  @JsonProperty("properties")
  @JsonInclude(Include.NON_EMPTY)
  public Map<String, Object> properties()
  {
    return properties;
  }

  public void validate()
  {
    if (Strings.isNullOrEmpty(name)) {
      throw new IAE("Column name is required");
    }
    if (Columns.isTimeColumn(name)) {
      if (dataType != null && !Columns.LONG.equalsIgnoreCase(dataType)) {
        throw new IAE(
            "[%s] column must have type [%s] or no type. Found [%s]",
            name,
            Columns.LONG,
            dataType
        );
      }
    }
    // Validate type in the next PR
  }

  /**
   * Merges an updated version of this column with an existing version.
   * <p>
   * The name cannot be changed (it is what links the existing column and the
   * update). The SQL type will be that provided in the update, if non-null, else
   * the original type. Properties are merged using standard rules: those in the
   * update take precedence. Null values in the update remove the existing property,
   * non-null values update the property. Any properties in the update but not in

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Set the __time column dataType to "long", or remove the dataType property entirely.
  2. Express timestamp formatting in the ingestion timestampSpec instead of typing the column as timestamp.
  3. Search the spec for case mismatches — "LONG" is accepted case-insensitively, but "timestamp" is not.

Example fix

// before
{"name": "__time", "dataType": "timestamp"}
// after
{"name": "__time", "dataType": "long"}
Defensive patterns

Strategy: validation

Validate before calling

if ("__time".equals(spec.getName()) && spec.getDataType() != null
    && !"long".equalsIgnoreCase(spec.getDataType())) {
  throw new IllegalArgumentException("__time dataType must be long or omitted");
}

Type guard

boolean isValidTimeColumnType(ColumnSpec spec) {
  return !Columns.isTimeColumn(spec.getName())
      || spec.getDataType() == null
      || Columns.LONG.equalsIgnoreCase(spec.getDataType());
}

Try / catch

try { spec.validate(); } catch (IllegalArgumentException e) { // correct the __time type to long or drop dataType }

Prevention

When it happens

Trigger: Calling validate() (directly or via mergeColumn/testColumnSpec) on a ColumnSpec named __time whose dataType is set to anything other than "long" (case-insensitive), e.g. "string" or "timestamp".

Common situations: Users coming from SQL intuition set the __time column type to "timestamp" or "date"; schema import from another system carries over a DATETIME/TIMESTAMP type for the time column.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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