{"record":{"id":"acf81bbd40dd134c","repo":"apache/druid","slug":"column-d-must-have-a-name","errorCode":null,"errorMessage":"Column %d must have a name","messagePattern":"Column (.+?) must have a name","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/catalog/model/TableDefn.java","lineNumber":171,"sourceCode":"   * Columns are matched by name. If the column exists, then it is updated. If\n   * the column does not exist, then the new column is appended to the existing\n   * list. This merge operation cannot remove columns or change order.\n   */\n  public List<ColumnSpec> mergeColumns(List<ColumnSpec> columns, List<ColumnSpec> update)\n  {\n    if (update == null || update.isEmpty()) {\n      return columns;\n    }\n    Map<String, Integer> original = new HashMap<>();\n    for (int i = 0; i < columns.size(); i++) {\n      original.put(columns.get(i).name(), i);\n    }\n    List<ColumnSpec> merged = new ArrayList<>(columns);\n    for (int i = 0; i < update.size(); i++) {\n      ColumnSpec col = update.get(i);\n      String colName = col.name();\n      if (Strings.isNullOrEmpty(colName)) {\n        throw new IAE(\"Column %d must have a name\", i + 1);\n      }\n      Integer index = original.get(col.name());\n      if (index == null) {\n        merged.add(col);\n      } else {\n        merged.set(index, mergeColumn(columns.get(index), col));\n      }\n    }\n    return merged;\n  }\n\n  private ColumnSpec mergeColumn(ColumnSpec existingCol, ColumnSpec update)\n  {\n    ColumnSpec revised = existingCol.merge(columnProperties, update);\n    revised.validate();\n    validateColumn(revised);\n    return revised;\n  }","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/catalog/model/TableDefn.java#L153-L189","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set a non-empty name on every column in the update spec before merging","Fix the JSON payload so each update column object includes a \"name\" field","Reject/validate the update payload client-side before calling merge"],"exampleFix":"// before\nList<ColumnSpec> update = Collections.singletonList(new ColumnSpec(null, \"long\"));\nspec.merge(base, new TableSpec(null, null, update), mapper);\n// after\nList<ColumnSpec> update = Collections.singletonList(new ColumnSpec(\"metric1\", \"long\"));\nspec.merge(base, new TableSpec(null, null, update), mapper);","handlingStrategy":"validation","validationCode":"for (int i = 0; i < update.columns().size(); i++) {\n  if (Strings.isNullOrEmpty(update.columns().get(i).name())) {\n    throw new IllegalArgumentException(\"update column \" + (i + 1) + \" has no name\");\n  }\n}","typeGuard":"boolean allColumnsNamed(List<ColumnSpec> cols) {\n  return cols != null && cols.stream().allMatch(c -> c.name() != null && !c.name().isEmpty());\n}","tryCatchPattern":"try { return base.merge(base, update, mapper); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"must have a name\")) { /* repair update columns */ } else { throw e; } }","preventionTips":["Require name on every column constructor/DTO","Reject anonymous columns at deserialization time","Include column-name checks in your update-payload builder"],"tags":["validation","merge","missing-field"],"backgroundTag":"empty-required-field","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}