apache/iceberg · error · IcebergAnalysisException
ALTER TABLE contains multiple ordering clauses
Error message
ALTER TABLE contains multiple ordering clauses
What it means
Analogous to the distribution check: a WRITE spec may carry at most one ordering clause, and the AST builder throws this IcebergAnalysisException when writeSpec.writeOrderingSpec contains more than one. Two ordering clauses would leave the resulting SortOrder ambiguous, so the statement is rejected at parse/AST-build time.
Solutions
- Keep a single ordering clause listing all sort keys: `ORDERED BY a, b` instead of two clauses.
- Choose either global `ORDERED BY` or `LOCALLY ORDERED BY` per statement, not both.
- Regenerate the DDL from one source of truth if tooling emitted duplicate clauses.
Example fix
-- before ALTER TABLE t WRITE ORDERED BY a LOCALLY ORDERED BY b -- after ALTER TABLE t WRITE ORDERED BY a, b
Defensive patterns
Strategy: validation
Validate before calling
val orderCount = "(LOCALLY\\s+)?ORDERED\\s+BY".r.findAllMatchIn(writeSpec).size require(orderCount <= 1, "Only one WRITE ordering clause is allowed")
Prevention
- List all sort keys in a single ORDERED BY clause
- Never combine ORDERED BY with LOCALLY ORDERED BY in one statement
- Edit-in-place DDL: remove the old ordering clause when adding a new one
When it happens
Trigger: Executing `ALTER TABLE t WRITE ORDERED BY a ORDERED BY b`, or combining `ORDERED BY` with `LOCALLY ORDERED BY` in the same WRITE spec.
Common situations: Generated DDL appending default and user ordering; hand-editing an ALTER statement and leaving both the old and new ORDERED BY clauses in place.
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…
- 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/fe7893222aa05ef5.
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: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)