apache/iceberg · error · UnsupportedOperationException
Cannot replace partition field in non-Iceberg table: $table
Error message
Cannot replace partition field in non-Iceberg table: $table
What it means
ReplacePartitionFieldExec throws this when ALTER TABLE ... REPLACE PARTITION FIELD targets a non-Iceberg table. Replacing a partition field (removing the old transform and adding a new one via UpdateSpec) is Iceberg partition-evolution functionality; other V2 table implementations do not support it, so the executor throws UnsupportedOperationException.
Solutions
- Qualify the statement with the Iceberg catalog so it resolves to an Iceberg table
- Confirm both the from and to transforms are valid Iceberg terms (Spark3Util.toIcebergTerm must translate them)
- For non-Iceberg tables, use that engine's partition-management features or migrate to Iceberg
Example fix
// before ALTER TABLE delta_t REPLACE PARTITION FIELD days(ts) WITH month(ts); // after ALTER TABLE iceberg_catalog.db.t REPLACE PARTITION FIELD days(ts) WITH month(ts);
Defensive patterns
Strategy: validation
Validate before calling
val loaded = catalog.loadTable(ident) require(loaded.isInstanceOf[org.apache.iceberg.spark.source.SparkTable], s"REPLACE PARTITION FIELD requires Iceberg: $ident")
Type guard
def isIcebergTable(t: org.apache.spark.sql.connector.catalog.Table): Boolean = t.isInstanceOf[org.apache.iceberg.spark.source.SparkTable]
Try / catch
try { spark.sql(s"ALTER TABLE $ident REPLACE PARTITION FIELD days(ts) WITH month(ts)") } catch { case e: UnsupportedOperationException if e.getMessage.contains("non-Iceberg") => log.error(s"Partition evolution unsupported on $ident") } Prevention
- Confirm table format before partition-evolution DDL
- Qualify the table with the Iceberg catalog
- Keep Iceberg DDL in format-specific migration scripts
When it happens
Trigger: Running `ALTER TABLE ... REPLACE PARTITION FIELD days(ts) WITH month(ts)` where the table resolves through a non-Iceberg catalog and the `iceberg: SparkTable` match fails.
Common situations: Partition-granularity change scripts applied to Delta/Hive tables; wrong catalog binding in multi-catalog sessions; migrating pipelines between table formats.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Cannot drop partition field in non-Iceberg table: $table
- Cannot drop branch on non-Iceberg table: $table
- Cannot drop identifier fields in non-Iceberg table: $table
- Cannot drop tag on non-Iceberg table: $table
- Cannot set identifier fields in non-Iceberg table: $table
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/32717a978ebbd3c0.
Report an issue: GitHub.
Appendix: source
Thrown at spark/v4.1/spark-extensions/src/main/scala/org/apache/spark/sql/execution/datasources/v2/ReplacePartitionFieldExec.scala:65
case IdentityTransform(FieldReference(parts))
if parts.size == 1 && schema.findField(parts.head) == null =>
// the name is not present in the Iceberg schema, so it must be a partition field name, not a column name
iceberg.table
.updateSpec()
.removeField(parts.head)
.addField(name.orNull, Spark3Util.toIcebergTerm(transformTo))
.commit()
case _ =>
iceberg.table
.updateSpec()
.removeField(Spark3Util.toIcebergTerm(transformFrom))
.addField(name.orNull, Spark3Util.toIcebergTerm(transformTo))
.commit()
}
case table =>
throw new UnsupportedOperationException(
s"Cannot replace partition field in non-Iceberg table: $table")
}
Nil
}
override def simpleString(maxFields: Int): String = {
s"ReplacePartitionField ${catalog.name}.${ident.quoted} ${transformFrom.describe} " +
s"with ${name.map(n => s"$n=").getOrElse("")}${transformTo.describe}"
}
}
View on GitHub (pinned to 86d9c8fc54)