{"record":{"id":"8bba60070acc45e4","repo":"apache/iceberg","slug":"cannot-create-tag-to-non-iceberg-table-table-8bba60","errorCode":null,"errorMessage":"Cannot create tag to non-Iceberg table: $table","messagePattern":"Cannot create tag to non-Iceberg table: \\$table","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.1/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/v4.1/spark-extensions/src/main/scala/org/apache/spark/sql/execution/datasources/v2/CreateOrReplaceTagExec.scala#L59-L87","documentation":"CreateOrReplaceTagExec commits tag creation via manageSnapshot only for Iceberg tables. When the resolved table is not an Iceberg table, the catch-all case throws this UnsupportedOperationException, since tags (named snapshot references) are an Iceberg-only feature.","triggerScenarios":"Running `ALTER TABLE ... CREATE TAG ...` (or CREATE OR REPLACE TAG) against a table backed by a non-Iceberg catalog or format.","commonSituations":"Forgetting the Iceberg catalog prefix so the table resolves via spark_catalog, targeting Delta/Parquet tables, or scripts that iterate mixed-format tables and attempt tagging on all of them.","solutions":["Prefix the table with its Iceberg catalog name: `ALTER TABLE iceberg_catalog.db.t CREATE TAG tag1 ...`.","Check spark.sql.catalog.* configuration to ensure the intended catalog is Iceberg's SparkCatalog/SparkSessionCatalog.","Confirm the table is Iceberg-format; if not, migrate or skip it in batch scripts.","Guard batch scripts by checking the table format (via DESCRIBE TABLE / catalog API) before issuing tag DDL."],"exampleFix":"-- before\nALTER TABLE spark_catalog.db.t CREATE TAG v1\n-- after\nALTER TABLE iceberg_catalog.db.t CREATE TAG v1","handlingStrategy":"type-guard","validationCode":"// Scala: guard tag DDL behind an Iceberg-catalog check\nval ok = spark.conf.get(s\"spark.sql.catalog.iceberg_catalog\", \"\")\n  .contains(\"org.apache.iceberg.spark.SparkCatalog\")","typeGuard":"// Scala\n table match {\n  case _: org.apache.iceberg.spark.SparkTable => true\n  case _ => false // cannot tag non-Iceberg tables\n}","tryCatchPattern":"// Scala\ntry {\n  spark.sql(s\"ALTER TABLE $ident CREATE TAG $tag\")\n} catch {\n  case _: UnsupportedOperationException => log.warn(s\"Skipping tag on $ident: not an Iceberg table\")\n}","preventionTips":["Prefix table identifiers with the Iceberg catalog name.","In batch tagging scripts, check the table's format first and skip non-Iceberg entries.","Keep one Iceberg catalog configured and consistently named across jobs."],"tags":["spark","tags","non-iceberg-table","unsupported-operation"],"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"}