apache/iceberg · error · IcebergAnalysisException

ALTER TABLE has no changes: missing both distribution and…

Error message

ALTER TABLE has no changes: missing both distribution and ordering clauses

What it means

The AST builder for `ALTER TABLE ... WRITE (DISTRIBUTED ... | ORDERED ...)` requires at least one of a distribution clause or an ordering clause; if writeSpec contains neither, the statement would silently be a no-op, so Iceberg rejects it with this IcebergAnalysisException.

Solutions

  1. Add a distribution clause, e.g. `ALTER TABLE t WRITE DISTRIBUTED BY PARTITION` or `WRITE DISTRIBUTED BY HASH (col)`.
  2. Or add an ordering clause, e.g. `ALTER TABLE t WRITE ORDERED BY ts`.
  3. Or specify both: `ALTER TABLE t WRITE DISTRIBUTED BY PARTITION ORDERED BY ts`.
  4. To reset write distribution/ordering, use `ALTER TABLE t WRITE UNORDERED` / `WRITE NONE` forms instead of omitting the clause.

Example fix

-- before
ALTER TABLE my_table WRITE
-- after
ALTER TABLE my_table WRITE DISTRIBUTED BY PARTITION ORDERED BY ts
Defensive patterns

Strategy: validation

Validate before calling

// Reject empty WRITE specs before submitting
val stmt = s"ALTER TABLE $table WRITE $writeSpec"
require(writeSpec.nonEmpty, "ALTER TABLE ... WRITE requires a DISTRIBUTED and/or ORDERED clause")
spark.sql(stmt)

Try / catch

try { spark.sql(stmt) } catch {
  case e: IcebergAnalysisException if e.getMessage.contains("missing both distribution and ordering") =>
    spark.sql(s"ALTER TABLE $table WRITE UNORDERED")
}

Prevention

When it happens

Trigger: Executing `ALTER TABLE t WRITE` (or a WRITE spec with an empty body) without any `DISTRIBUTED BY ...`/`PARTITIONED BY ...` hash clause and without any `ORDERED BY ...`/`LOCALLY ORDERED BY ...` clause.

Common situations: Copy-pasted ALTER statements where the clause part was deleted; scripted DDL generation emitting an empty write-spec; users expecting `WRITE` alone to reset distribution/ordering to defaults.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/fdb795af31b12617. Report an issue: GitHub.

Appendix: source

Thrown at spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/parser/extensions/IcebergSqlExtensionsAstBuilder.scala:230

  override def visitDropIdentifierFields(ctx: DropIdentifierFieldsContext): DropIdentifierFields =
    withOrigin(ctx) {
      DropIdentifierFields(
        typedVisit[Seq[String]](ctx.multipartIdentifier),
        toSeq(ctx.fieldList.fields).map(_.getText))
    }

  /**
   * Create a [[SetWriteDistributionAndOrdering]] for changing the write distribution and ordering.
   */
  override def visitSetWriteDistributionAndOrdering(
      ctx: SetWriteDistributionAndOrderingContext): SetWriteDistributionAndOrdering = {

    val tableName = typedVisit[Seq[String]](ctx.multipartIdentifier)

    val (distributionSpec, orderingSpec) = toDistributionAndOrderingSpec(ctx.writeSpec)

    if (distributionSpec == null && orderingSpec == null) {
      throw new IcebergAnalysisException(
        "ALTER TABLE has no changes: missing both distribution and ordering clauses")
    }

    val distributionMode = if (distributionSpec != null) {
      Some(DistributionMode.HASH)
    } else if (orderingSpec.UNORDERED != null) {
      Some(DistributionMode.NONE)
    } else if (orderingSpec.LOCALLY() != null) {
      None
    } else {
      Some(DistributionMode.RANGE)
    }

    val ordering = if (orderingSpec != null && orderingSpec.order != null) {
      toSeq(orderingSpec.order.fields).map(typedVisit[(Term, SortDirection, NullOrder)])
    } else {
      Seq.empty
    }

View on GitHub (pinned to 86d9c8fc54)