{"record":{"id":"6f94be00ad76da82","repo":"apache/iceberg","slug":"cannot-create-tag-to-non-iceberg-table-table","errorCode":null,"errorMessage":"Cannot create tag to non-Iceberg table: $table","messagePattern":"Cannot create tag to non-Iceberg table: \\$table","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"spark/v3.5/spark-extensions/src/main/scala/org/apache/spark/sql/execution/datasources/v2/CreateOrReplaceTagExec.scala","lineNumber":77,"sourceCode":"          manageSnapshot.createTag(tag, snapshotId)\n        } else if (replace) {\n          manageSnapshot.replaceTag(tag, snapshotId)\n        } else {\n          if (refExists && ifNotExists) {\n            return Nil\n          }\n\n          manageSnapshot.createTag(tag, snapshotId)\n        }\n\n        if (tagOptions.snapshotRefRetain.nonEmpty) {\n          manageSnapshot.setMaxRefAgeMs(tag, tagOptions.snapshotRefRetain.get)\n        }\n\n        manageSnapshot.commit()\n\n      case table =>\n        throw new UnsupportedOperationException(s\"Cannot create tag to non-Iceberg table: $table\")\n    }\n\n    Nil\n  }\n\n  override def simpleString(maxFields: Int): String = {\n    s\"Create tag: $tag for table: ${ident.quoted}\"\n  }\n}\n","sourceCodeStart":59,"sourceCodeEnd":87,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v3.5/spark-extensions/src/main/scala/org/apache/spark/sql/execution/datasources/v2/CreateOrReplaceTagExec.scala#L59-L87","documentation":"CreateOrReplaceTagExec throws this UnsupportedOperationException when a CREATE OR REPLACE TAG statement targets a non-Iceberg table. Tags are an Iceberg snapshot-reference feature that requires the underlying SparkTable to expose Iceberg's manageSnapshots API, so the plan only executes for SparkTable instances wrapping an Iceberg table. Any other table provider reaches the fallback case and fails.","triggerScenarios":"Executing CREATE OR REPLACE TAG <tag> [AS OF VERSION n] [RETAIN ...] on a table resolved from a catalog where the table is not an Iceberg SparkTable.","commonSituations":"Mistyping the catalog so the DDL lands on a Hive/Delta table; running tag DDL in a session where the default catalog is not the Iceberg catalog; assuming all tables in a mixed-provider namespace support tags.","solutions":["Check the table provider with DESC TABLE EXTENDED and confirm it is iceberg.","Fully qualify the statement with the Iceberg catalog name: CREATE OR REPLACE TAG t IN iceberg_catalog.db.tbl.","Configure spark.sql.catalog.<name> to SparkCatalog so Iceberg identifiers resolve to SparkTable.","Migrate the table to Iceberg if tags are required."],"exampleFix":"-- before\nCREATE OR REPLACE TAG v1 AS OF VERSION 10 IN db.events; -- non-Iceberg\n-- after\nCREATE OR REPLACE TAG v1 AS OF VERSION 10 IN iceberg_catalog.db.events;","handlingStrategy":"validation","validationCode":"assert(spark.sessionState.catalogManager.currentCatalog.name() == \"iceberg_catalog\", \"must USE the Iceberg catalog before tag DDL\")","typeGuard":"def supportsTags(t: org.apache.spark.sql.connector.catalog.Table): Boolean = t match { case s: org.apache.iceberg.spark.source.SparkTable => s.table().ops().current() != null; case _ => false }","tryCatchPattern":"try { spark.sql(\"CREATE OR REPLACE TAG v1 IN cat.db.tbl\") } catch { case e: UnsupportedOperationException if e.getMessage.contains(\"non-Iceberg table\") => log.warn(\"tag DDL requires an Iceberg table\") }","preventionTips":["USE the Iceberg catalog before running tag DDL","Keep tags in scripts scoped to explicitly Iceberg-qualified table names","Avoid mixing providers in namespaces that receive tag DDL"],"tags":["spark","iceberg","unsupported-operation","tags"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}