apache/iceberg · error
ALTER TABLE contains multiple distribution clauses
Error message
ALTER TABLE contains multiple distribution clauses
What it means
The grammar permits a list of distribution spec tokens, but Iceberg only supports a single distribution clause per ALTER TABLE ... WRITE statement. More than one triggers an AnalysisException so the statement cannot be ambiguously interpreted.
Solutions
- Keep exactly one distribution clause — combine intent into it, e.g. ALTER TABLE t WRITE DISTRIBUTED BY PARTITION;
- Use HASH(col) as the single clause if keyed distribution is desired
- Split into sequential ALTER TABLE statements if transitioning between modes is intended
Example fix
// before ALTER TABLE t WRITE DISTRIBUTED BY PARTITION HASH(id); // after ALTER TABLE t WRITE HASH(id);
Defensive patterns
Strategy: validation
Validate before calling
val distCount = "DISTRIBUTED\\s+BY|HASH\\(|RANGE\\(|".r.findAllIn(sqlText).size require(distCount <= 1, "Only one distribution clause allowed")
Try / catch
try { spark.sql(ddl) } catch { case e: AnalysisException if e.getMessage.contains("multiple distribution") => log.error("duplicate distribution clause"); throw e } Prevention
- Emit at most one distribution clause in generated DDL
- Prefer a single DISTRIBUTED BY PARTITION or HASH(col) clause
- Review concatenated SQL fragments for duplicated clauses
When it happens
Trigger: Writing 'ALTER TABLE t WRITE DISTRIBUTED BY PARTITION HASH(id)' or otherwise stacking two writeDistributionSpec clauses in one statement.
Common situations: Users combining DISTRIBUTED BY PARTITION with a second HASH(col) clause expecting both to apply; auto-generated SQL concatenating clauses.
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 distribution clauses
- ALTER TABLE contains multiple ordering clauses
- ALTER TABLE contains multiple ordering clauses
- ALTER TABLE has no changes: missing both distribution and…
- ALTER TABLE has no changes: missing both distribution and…
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/e46331c72e2c9217.
Report an issue: GitHub.
Appendix: source
Thrown at spark/v3.5/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/parser/extensions/IcebergSqlExtensionsAstBuilder.scala:270
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 AnalysisException("ALTER TABLE contains multiple distribution clauses")
}
if (writeSpec.writeOrderingSpec.size > 1) {
throw new AnalysisException("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)