apache/iceberg · error · IcebergAnalysisException
ALTER TABLE contains multiple ordering clauses
Error message
ALTER TABLE contains multiple ordering clauses
What it means
Symmetric to the distribution check, the AstBuilder rejects a WRITE spec containing more than one ordering clause. At most one ORDERED BY / LOCALLY ORDERED BY clause is allowed per ALTER TABLE ... WRITE statement.
Solutions
- Use a single ORDERED BY with multiple columns: `ORDERED BY a, b`.
- Remove the duplicated ordering clause and keep one.
- Choose either global ORDERED BY or LOCALLY ORDERED BY, not both/repeated.
Example fix
-- before ALTER TABLE t WRITE ORDERED BY a ORDERED BY b -- after ALTER TABLE t WRITE ORDERED BY a, b
Defensive patterns
Strategy: validation
Validate before calling
// keep ordering as one list rendered into a single clause require(orderingDirectives.size <= 1, "at most one ORDERED BY / LOCALLY ORDERED BY clause")
Prevention
- Express multi-key ordering as ORDERED BY a, b, not repeated clauses.
- Choose exactly one of ORDERED BY or LOCALLY ORDERED BY.
- Dedupe ordering directives in SQL builders.
When it happens
Trigger: Running `ALTER TABLE t WRITE ORDERED BY a ORDERED BY b` or mixing ORDERED BY with LOCALLY ORDERED BY twice in one statement.
Common situations: Programmatically generated SQL appending multiple ordering directives, copy-paste of two ALTER statements merged into one, or attempting to express multi-key ordering with repeated clauses instead of a comma-separated column list.
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 has no changes: missing both distribution and…
- ALTER TABLE contains multiple distribution clauses
- 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/1be6b8cca94797a8.
Report an issue: GitHub.
Appendix: source
Thrown at spark/v4.1/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/parser/extensions/IcebergSqlExtensionsAstBuilder.scala:261
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)
.map(_ => SortDirection.ASC)
.orElse(Option(ctx.DESC).map(_ => SortDirection.DESC))
.getOrElse(SortDirection.ASC)
val nullOrder = Option(ctx.FIRST)View on GitHub (pinned to 86d9c8fc54)