apache/iceberg · error

Cannot set identifier fields in non-Iceberg table: $table

Error message

Cannot set identifier fields in non-Iceberg table: $table

What it means

SetIdentifierFieldsExec throws this UnsupportedOperationException when a SET IDENTIFIER FIELDS statement targets a non-Iceberg table. Setting identifier fields requires Iceberg's updateSchema().setIdentifierFields() API, exposed only by SparkTable instances wrapping Iceberg tables. Any other resolved table reaches the fallback throwing case.

Solutions

  1. Verify the provider with DESC TABLE EXTENDED and confirm iceberg.
  2. Qualify the statement with the Iceberg catalog: ALTER TABLE iceberg_catalog.db.tbl SET IDENTIFIER FIELDS id.
  3. Register the catalog as org.apache.iceberg.spark.SparkCatalog so identifiers produce SparkTables.
  4. Migrate the table to Iceberg if identifier fields are needed for row-level operations.

Example fix

-- before
ALTER TABLE db.events SET IDENTIFIER FIELDS event_id; -- non-Iceberg
-- after
ALTER TABLE iceberg_catalog.db.events SET IDENTIFIER FIELDS event_id;
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", "SET IDENTIFIER FIELDS requires Iceberg")

Type guard

def supportsIdentifierFields(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 SET IDENTIFIER FIELDS event_id") } catch { case e: UnsupportedOperationException if e.getMessage.contains("non-Iceberg") => log.warn("identifier fields require an Iceberg table") }

Prevention

When it happens

Trigger: Running ALTER TABLE ... SET IDENTIFIER FIELDS <cols> where the resolved table does not match `iceberg: SparkTable`, so the pattern match fails.

Common situations: Identifier-field DDL executed on Hive/Delta tables; wrong catalog binding so the command resolves through a non-Iceberg provider; teams rolling out row-level-dedupe features onto tables not yet migrated to Iceberg.

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/fd939ccd9f2eb71e. Report an issue: GitHub.

Appendix: source

Thrown at spark/v3.5/spark-extensions/src/main/scala/org/apache/spark/sql/execution/datasources/v2/SetIdentifierFieldsExec.scala:42

import org.apache.spark.sql.connector.catalog.Identifier
import org.apache.spark.sql.connector.catalog.TableCatalog
import scala.jdk.CollectionConverters._

case class SetIdentifierFieldsExec(catalog: TableCatalog, ident: Identifier, fields: Seq[String])
    extends LeafV2CommandExec {
  import org.apache.spark.sql.connector.catalog.CatalogV2Implicits._

  override lazy val output: Seq[Attribute] = Nil

  override protected def run(): Seq[InternalRow] = {
    catalog.loadTable(ident) match {
      case iceberg: SparkTable =>
        iceberg.table
          .updateSchema()
          .setIdentifierFields(fields.asJava)
          .commit();
      case table =>
        throw new UnsupportedOperationException(
          s"Cannot set identifier fields in non-Iceberg table: $table")
    }

    Nil
  }

  override def simpleString(maxFields: Int): String = {
    s"SetIdentifierFields ${catalog.name}.${ident.quoted} (${fields.quoted})";
  }
}

View on GitHub (pinned to 86d9c8fc54)