{"record":{"id":"48c0f1ac9a87d651","repo":"apache/iceberg","slug":"cannot-use-non-v1-table-s-as-a-source","errorCode":null,"errorMessage":"Cannot use non-v1 table '%s' as a source","messagePattern":"Cannot use non-v1 table '(.+?)' as a source","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/actions/BaseTableCreationSparkAction.java","lineNumber":83,"sourceCode":"  private final Identifier sourceTableIdent;\n\n  // Optional Parameters for destination\n  private final Map<String, String> additionalProperties = Maps.newHashMap();\n\n  BaseTableCreationSparkAction(\n      SparkSession spark, CatalogPlugin sourceCatalog, Identifier sourceTableIdent) {\n    super(spark);\n\n    this.sourceCatalog = checkSourceCatalog(sourceCatalog);\n    this.sourceTableIdent = sourceTableIdent;\n\n    try {\n      this.sourceTable = (V1Table) this.sourceCatalog.loadTable(sourceTableIdent);\n      this.sourceCatalogTable = sourceTable.v1Table();\n    } catch (org.apache.spark.sql.catalyst.analysis.NoSuchTableException e) {\n      throw new NoSuchTableException(\"Cannot find source table '%s'\", sourceTableIdent);\n    } catch (ClassCastException e) {\n      throw new IllegalArgumentException(\n          String.format(\"Cannot use non-v1 table '%s' as a source\", sourceTableIdent), e);\n    }\n    validateSourceTable();\n\n    this.sourceTableLocation =\n        CatalogUtils.URIToString(sourceCatalogTable.storage().locationUri().get());\n  }\n\n  protected abstract TableCatalog checkSourceCatalog(CatalogPlugin catalog);\n\n  protected abstract StagingTableCatalog destCatalog();\n\n  protected abstract Identifier destTableIdent();\n\n  protected abstract Map<String, String> destTableProps();\n\n  protected String sourceTableLocation() {\n    return sourceTableLocation;","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/actions/BaseTableCreationSparkAction.java#L65-L101","documentation":"When loading the source table, BaseTableCreationSparkAction casts the result to Spark's V1Table. If the catalog returns a V2Table or other catalog implementation, the ClassCastException is translated into IllegalArgumentException 'Cannot use non-v1 table as a source'. These actions (snapshot/migrate) only support V1 Spark catalog tables as sources.","triggerScenarios":"Running migrate/snapshot with a source table that resolves through a V2 catalog (e.g. an Iceberg catalog session catalog, or any catalog plugin exposing V2Table), such as migrate from one Iceberg table to another.","commonSituations":"Trying to migrate an already-Iceberg table; source catalog is Spark's built-in v2 session catalog; Spark 3.x table resolution returning V2 tables for DataSource v2 providers.","solutions":["Use a source table registered in a V1 catalog (e.g. Hive/metastore tables via spark_catalog)","For Iceberg-to-Iceberg copies use DataFrame read/write or a CTAS/RTAS statement instead of migrate/snapshot","Point the source identifier at spark_catalog rather than a v2 session catalog"],"exampleFix":"// before\nSparkActions.get(spark).migrate(\"iceberg_catalog\", \"db.tbl\").execute(); // v2 source\n// after\nSparkActions.get(spark).migrate(\"spark_catalog\", \"db.parquet_tbl\").execute();","handlingStrategy":"validation","validationCode":"CatalogPlugin plugin = spark.sessionState().catalogManager().catalog(catalogName);\n// migrate/snapshot sources must resolve to V1Table; check the provider first\nString provider = spark.conf().get(\"spark.sql.catalog.\" + catalogName, \"\");","typeGuard":"boolean isV1Source(CatalogManager cm, String catalog) { return cm.catalog(catalog).loadTable(ident) instanceof V1Table; }","tryCatchPattern":"try { action.execute(); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"non-v1 table\")) { /* use spark_catalog or a copy strategy */ } throw e; }","preventionTips":["Only run migrate/snapshot against tables in a V1 catalog (spark_catalog backed by Hive/metastore)","For Iceberg-to-Iceberg moves, use CTAS/RTAS or DataFrame copy instead","Check the catalog provider class before choosing the action"],"tags":["spark","catalog","actions"],"backgroundTag":"incompatible-source-type","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}