apache/druid · error · CatalogException

CatalogException.validationError(e) (wraps IAE/DruidExceptio

Error message

CatalogException.validationError(e) (wraps IAE/DruidException validation message)

What it means

TableEditor.applyUpdateColumns merges column updates into the existing spec and validates the revised column list with defn.validateColumns(revised); failures (IAE/DruidException) are wrapped in CatalogException.validationError(e). This guards against updates that would leave the table's columns invalid (duplicates, type changes violating rules, etc.).

Source

Thrown at extensions-core/druid-catalog/src/main/java/org/apache/druid/catalog/http/TableEditor.java:317

    return catalog.tables().updateColumns(
        id,
        table -> applyUpdateColumns(table, updates)
    );
  }

  private TableSpec applyUpdateColumns(
      final TableMetadata table,
      final List<ColumnSpec> updates
  ) throws CatalogException
  {
    final TableSpec existingSpec = table.spec();
    final TableDefn defn = resolveDefn(existingSpec.type());
    final List<ColumnSpec> revised = defn.mergeColumns(existingSpec.columns(), updates);
    try {
      defn.validateColumns(revised);
    }
    catch (IAE | DruidException e) {
      throw CatalogException.validationError(e);
    }
    return existingSpec.withColumns(revised);
  }

  private long moveColumn(MoveColumn moveColumn) throws CatalogException
  {
    if (Strings.isNullOrEmpty(moveColumn.column)) {
      throw CatalogException.badRequest("A column name is required");
    }
    if (moveColumn.where == null) {
      throw CatalogException.badRequest("A target location is required");
    }
    if ((moveColumn.where == Position.BEFORE || moveColumn.where == Position.AFTER) && Strings.isNullOrEmpty(moveColumn.anchor)) {
      throw CatalogException.badRequest("A anchor column is required for BEFORE or AFTER");
    }
    return catalog.tables().updateColumns(
        id,
        table -> applyMoveColumn(table, moveColumn)

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Fix the offending column update per the wrapped validation message.
  2. Fetch the latest table spec first so merges are applied on a current column list.
  3. Split large column updates into smaller changes to isolate the invalid one.
  4. Re-run validation locally by constructing the TableDefn and calling validateColumns on the merged list.

Example fix

// before: update adds a column named 'ts' when 'ts' already exists
// after: rename new column to 'ts2' or drop/replace the existing one
Defensive patterns

Strategy: try-catch

Validate before calling

// Re-apply the merge locally and validate before submitting
List<ColumnSpec> merged = defn.mergeColumns(existingSpec.columns(), updates);
defn.validateColumns(merged); // throws before the server does

Try / catch

try { editor.updateColumns(tableId, updates); }
catch (CatalogException e) { if (e.isValidation()) { refreshSpecAndRetryWithFixedUpdates(e); } else { throw e; } }

Prevention

When it happens

Trigger: Submitting an updateColumns operation to the table editor where the merged column list fails definition-level validation, e.g. renaming to an existing column name, changing a column type in a disallowed way, or dropping a required column.

Common situations: Concurrent edits based on stale column lists; batch updates combining several column changes where one is invalid; tooling that generates column diffs with conflicting names.

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/a6d0a6069aed9209. Report an issue: GitHub.