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

  1. Confirm the table provider is iceberg via DESC TABLE EXTENDED.
  2. Qualify with the Iceberg catalog: ALTER TABLE iceberg_catalog.db.tbl REPLACE PARTITION FIELD days(ts) WITH hours(ts).
  3. Register the catalog with org.apache.iceberg.spark.SparkCatalog.
  4. 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

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


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)