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
- Add a distribution clause, e.g. `ALTER TABLE t WRITE DISTRIBUTED BY PARTITION` or `WRITE DISTRIBUTED BY HASH (col)`.
- Or add an ordering clause, e.g. `ALTER TABLE t WRITE ORDERED BY ts`.
- Or specify both: `ALTER TABLE t WRITE DISTRIBUTED BY PARTITION ORDERED BY ts`.
- 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
- Always include DISTRIBUTED BY, PARTITION, ORDERED BY, LOCALLY ORDERED BY, or UNORDERED in WRITE specs
- Use WRITE UNORDERED/NONE explicitly when resetting instead of omitting clauses
- Template DDL generators to emit a default clause
- Review generated ALTER statements for empty spec bodies
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
- ALTER TABLE contains multiple distribution clauses
- ALTER TABLE contains multiple ordering clauses
- ALTER TABLE has no changes: missing both distribution and…
- Already closed files for partition:
- ALTER TABLE contains multiple distribution clauses
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)