apache/druid · error · IllegalArgumentException

Column %d must have a name

Error message

Column %d must have a name

What it means

When merging column updates, each update column must carry a non-empty name so the merge can locate the existing column it replaces (or append it as new). An unnamed column cannot be positioned in the merged list, so mergeColumns() throws IAE with the 1-based column position.

Source

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

   * 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;
    }
    Map<String, Integer> original = new HashMap<>();
    for (int i = 0; i < columns.size(); i++) {
      original.put(columns.get(i).name(), i);
    }
    List<ColumnSpec> merged = new ArrayList<>(columns);
    for (int i = 0; i < update.size(); i++) {
      ColumnSpec col = update.get(i);
      String colName = col.name();
      if (Strings.isNullOrEmpty(colName)) {
        throw new IAE("Column %d must have a name", i + 1);
      }
      Integer index = original.get(col.name());
      if (index == null) {
        merged.add(col);
      } else {
        merged.set(index, mergeColumn(columns.get(index), col));
      }
    }
    return merged;
  }

  private ColumnSpec mergeColumn(ColumnSpec existingCol, ColumnSpec update)
  {
    ColumnSpec revised = existingCol.merge(columnProperties, update);
    revised.validate();
    validateColumn(revised);
    return revised;
  }

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Set a non-empty name on every column in the update spec before merging
  2. Fix the JSON payload so each update column object includes a "name" field
  3. Reject/validate the update payload client-side before calling merge

Example fix

// before
List<ColumnSpec> update = Collections.singletonList(new ColumnSpec(null, "long"));
spec.merge(base, new TableSpec(null, null, update), mapper);
// after
List<ColumnSpec> update = Collections.singletonList(new ColumnSpec("metric1", "long"));
spec.merge(base, new TableSpec(null, null, update), mapper);
Defensive patterns

Strategy: validation

Validate before calling

for (int i = 0; i < update.columns().size(); i++) {
  if (Strings.isNullOrEmpty(update.columns().get(i).name())) {
    throw new IllegalArgumentException("update column " + (i + 1) + " has no name");
  }
}

Type guard

boolean allColumnsNamed(List<ColumnSpec> cols) {
  return cols != null && cols.stream().allMatch(c -> c.name() != null && !c.name().isEmpty());
}

Try / catch

try { return base.merge(base, update, mapper); } catch (IllegalArgumentException e) { if (e.getMessage().contains("must have a name")) { /* repair update columns */ } else { throw e; } }

Prevention

When it happens

Trigger: Calling TableSpec.merge() (via mergeColumns) with an update spec whose columns list contains a ColumnSpec with a null or empty name; the message reports the 1-based position of the offending column.

Common situations: Constructing update columns programmatically and forgetting to set the name; JSON update payloads with a missing/empty "name" field; partial column definitions copied from a template.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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