apache/iceberg · error
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 UnsupportedOperationException when a REPLACE PARTITION FIELD statement targets a non-Iceberg table. Replacing a partition field uses Iceberg's updateSpec().removeField(...).addField(...) API, available only on SparkTable instances wrapping Iceberg tables. Other providers fall through to the throwing case.
Solutions
- Confirm the table provider is iceberg via DESC TABLE EXTENDED.
- Qualify with the Iceberg catalog: ALTER TABLE iceberg_catalog.db.tbl REPLACE PARTITION FIELD days(ts) WITH hours(ts).
- Register the catalog with org.apache.iceberg.spark.SparkCatalog.
- Filter scripts to Iceberg tables before applying partition evolution DDL.
Example fix
-- before ALTER TABLE db.events REPLACE PARTITION FIELD days(ts) WITH hours(ts); -- non-Iceberg -- after ALTER TABLE iceberg_catalog.db.events REPLACE PARTITION FIELD days(ts) WITH hours(ts);
Defensive patterns
Strategy: validation
Validate before calling
val provider = spark.sql("DESC TABLE EXTENDED cat.db.tbl").where("col_name = 'Provider'").first().getString(1)
require(provider == "iceberg", "REPLACE PARTITION FIELD requires Iceberg") Type guard
def supportsSpecEvolution(t: org.apache.spark.sql.connector.catalog.Table): Boolean = t.isInstanceOf[org.apache.iceberg.spark.source.SparkTable]
Try / catch
try { spark.sql("ALTER TABLE cat.db.tbl REPLACE PARTITION FIELD days(ts) WITH hours(ts)") } catch { case e: UnsupportedOperationException if e.getMessage.contains("non-Iceberg") => log.warn("target is not Iceberg") } Prevention
- Qualify with the Iceberg catalog name in all partition evolution DDL
- Assert provider before batch-running spec-change scripts
- Keep partition-evolution tooling tied to Iceberg catalogs only
When it happens
Trigger: Running ALTER TABLE ... REPLACE PARTITION FIELD <from> WITH <to> where the resolved table is not an Iceberg SparkTable, so the `case iceberg: SparkTable` match fails.
Common situations: Partition-evolution DDL run against Hive/Delta tables; incorrect default catalog in multi-catalog sessions; generated migration scripts applying Iceberg spec changes to all tables regardless of provider.
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 add partition field to non-Iceberg table: $table
- Cannot drop partition field in non-Iceberg table: $table
- Cannot add partition field to non-Iceberg table: $table
- Cannot convert predicate to SQL
- Cannot convert term to SQL
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/9eebe0d9313d16e2.
Report an issue: GitHub.
Appendix: source
Thrown at spark/v3.5/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)