apache/iceberg · error · IcebergAnalysisException
ALTER TABLE contains multiple distribution clauses
Error message
ALTER TABLE contains multiple distribution clauses
What it means
The grammar allows collecting multiple write-distribution specs into writeSpec.writeDistributionSpec, but a WRITE spec may define at most one distribution mode; the AST builder rejects any statement with more than one, since a table cannot simultaneously be written with two distribution strategies.
Solutions
- Keep exactly one distribution clause; delete the redundant `DISTRIBUTED BY ...` occurrence.
- Decide the desired mode (NONE / HASH / PARTITION) and express it in a single clause.
- If both a partition-based and hash-based intent exist, choose HASH with the explicit column list, e.g. `WRITE DISTRIBUTED BY HASH (col)`.
Example fix
-- before ALTER TABLE t WRITE DISTRIBUTED BY PARTITION DISTRIBUTED BY HASH(id) -- after ALTER TABLE t WRITE DISTRIBUTED BY HASH(id)
Defensive patterns
Strategy: validation
Validate before calling
val distCount = "DISTRIBUTED\\s+BY|DISTRIBUTED\\s+BY\\s+PARTITION|PARTITIONED\\s+BY\\s+PARTITION".r .findAllMatchIn(writeSpec).size require(distCount <= 1, "Only one WRITE distribution clause is allowed")
Prevention
- Build WRITE specs from a single source of truth so only one distribution clause can be emitted
- Replace existing clauses rather than appending when regenerating DDL
- Prefer the HASH(col) form with an explicit column list over stacking clauses
When it happens
Trigger: Executing an ALTER TABLE WRITE statement containing two distribution clauses, e.g. `ALTER TABLE t WRITE DISTRIBUTED BY PARTITION DISTRIBUTED BY HASH(col)`, or concatenating generated DDL fragments that each add a distribution clause.
Common situations: Programmatic DDL builders appending a default distribution plus a user-specified one; copy-paste merging of two ALTER statements.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- 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
- ALTER TABLE contains multiple distribution clauses
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/a149de033971e40a.
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:257
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
}
SetWriteDistributionAndOrdering(tableName, distributionMode, ordering)
}
private def toDistributionAndOrderingSpec(
writeSpec: WriteSpecContext): (WriteDistributionSpecContext, WriteOrderingSpecContext) = {
if (writeSpec.writeDistributionSpec.size > 1) {
throw new IcebergAnalysisException("ALTER TABLE contains multiple distribution clauses")
}
if (writeSpec.writeOrderingSpec.size > 1) {
throw new IcebergAnalysisException("ALTER TABLE contains multiple ordering clauses")
}
val distributionSpec = toBuffer(writeSpec.writeDistributionSpec).headOption.orNull
val orderingSpec = toBuffer(writeSpec.writeOrderingSpec).headOption.orNull
(distributionSpec, orderingSpec)
}
/**
* Create an order field.
*/
override def visitOrderField(ctx: OrderFieldContext): (Term, SortDirection, NullOrder) = {
val term = Spark3Util.toIcebergTerm(typedVisit[Transform](ctx.transform))
val direction = Option(ctx.ASC)View on GitHub (pinned to 86d9c8fc54)