apache/druid · error · IllegalArgumentException

The update type must be null or [%s]

Error message

The update type must be null or [%s]

What it means

TableSpec.merge() combines a base spec with an update spec. If the update specifies a table type, it must either be null (meaning 'unchanged') or exactly match the base spec's type. A mismatched update type would produce a merged spec of ambiguous identity, so the library rejects it.

Source

Thrown at server/src/main/java/org/apache/druid/catalog/model/TableDefn.java:142

          + " (such as [%s] or [%s])",
          colSpec.name(),
          colSpec.dataType(),
          Columns.SQL_BIGINT,
          ColumnType.LONG_ARRAY.asTypeString(),
          ColumnType.NESTED_DATA.asTypeString()
      );
    }
  }

  /**
   * Merge a table spec with an update. The merge affects both the properties and
   * the list of columns.
   */
  public TableSpec merge(TableSpec spec, TableSpec update, ObjectMapper jsonMapper)
  {
    String updateType = update.type();
    if (updateType != null && !spec.type().equals(updateType)) {
      throw new IAE("The update type must be null or [%s]", spec.type());
    }
    Map<String, Object> revisedProps = mergeProperties(spec.properties(), update.properties());
    List<ColumnSpec> revisedColumns = mergeColumns(spec.columns(), update.columns());
    TableSpec revisedSpec = new TableSpec(spec.type(), revisedProps, revisedColumns);
    validate(new ResolvedTable(this, revisedSpec, jsonMapper));
    return revisedSpec;
  }

  /**
   * Merge the set of columns from an existing spec and an update.
   * Columns are matched by name. If the column exists, then it is updated. If
   * the column does not exist, then the new column is appended to the existing
   * list. This merge operation cannot remove columns or change order.
   */
  public List<ColumnSpec> mergeColumns(List<ColumnSpec> columns, List<ColumnSpec> update)
  {
    if (update == null || update.isEmpty()) {
      return columns;

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Set the update spec's type field to null so the base type is preserved
  2. Ensure the update spec's type matches the base spec's type exactly
  3. Verify the update payload was created for the correct table before merging

Example fix

// before
TableSpec update = new TableSpec("datasource", props, columns); // base is "table"
spec.merge(base, update, mapper);
// after
TableSpec update = new TableSpec(null, props, columns); // type omitted in update
spec.merge(base, update, mapper);
Defensive patterns

Strategy: validation

Validate before calling

if (update.type() != null && !base.type().equals(update.type())) {
  throw new IllegalArgumentException("update type mismatch: " + update.type());
}

Type guard

boolean isCompatibleUpdate(TableSpec base, TableSpec update) {
  return update.type() == null || base.type().equals(update.type());
}

Try / catch

try { return base.merge(base, update, mapper); } catch (IllegalArgumentException e) { if (e.getMessage().contains("The update type must be")) { /* null out update type and retry */ } else { throw e; } }

Prevention

When it happens

Trigger: Calling TableSpec.merge(base, update, jsonMapper) where update.type() is non-null and does not equal spec.type() (e.g. merging an update built for a different table type).

Common situations: Client fetched an update spec for one table type and applied it to another; a deserialization default set a wrong type; copying update payloads between tables of different types.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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