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
When executing ALTER TABLE ... WRITE (DISTRIBUTED BY ... / ORDERED BY ...) spec changes, the AST builder requires at least one of a distribution clause or an ordering clause. If the writeSpec contains neither, it throws IcebergAnalysisException because the statement would be a no-op. Iceberg models the write distribution/order as a pair and refuses empty updates.
Solutions
- Add the intended clause, e.g. WRITE ORDERED BY col or WRITE DISTRIBUTED BY HASH
- To reset ordering, write explicit `WRITE UNORDERED`; to reset distribution use `WRITE DIRECTLY` or `WRITE DISTRIBUTED BY HASH` as appropriate
- Remove the statement entirely if no change is intended
Example fix
// before ALTER TABLE t WRITE; // after ALTER TABLE t WRITE ORDERED BY id;
Defensive patterns
Strategy: validation
Validate before calling
def hasWriteSpecBody(stmt: String): Boolean = {
val s = stmt.toUpperCase(Locale.ROOT)
s.contains("DISTRIBUTED") || s.contains("ORDERED BY") || s.contains("UNORDERED") || s.contains("DIRECTLY")
} Try / catch
try { spark.sql(stmt) } catch { case e: IcebergAnalysisException if e.getMessage.contains("has no changes") => log.warn("No-op ALTER TABLE WRITE spec skipped") } Prevention
- Always emit a DISTRIBUTED BY or ORDERED BY clause when generating WRITE spec DDL
- To clear settings use explicit UNORDERED/DIRECTLY rather than omitting the body
- Review generated SQL for truncated clauses
When it happens
Trigger: Running `ALTER TABLE t WRITE` with no DISTRIBUTED BY and no ORDERED BY clause, e.g. `ALTER TABLE t WRITE` or `ALTER TABLE t WRITE UNORDERED` malformed/omitted body.
Common situations: Truncated SQL generation from scripts/ORMs, assuming a bare `ALTER TABLE t WRITE` resets defaults, or accidentally deleting the clause body in a migration script.
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 distribution clauses
- ALTER TABLE contains multiple ordering clauses
- ALTER TABLE contains multiple ordering clauses
- ALTER TABLE has no changes: missing both distribution and…
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/ed93348c48013325.
Report an issue: GitHub.
Appendix: source
Thrown at spark/v4.0/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)