apache/iceberg · error · IllegalArgumentException

Cannot use non-v1 table '%s' as a source

Error message

Cannot use non-v1 table '%s' as a source

What it means

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.

Source

Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/actions/BaseTableCreationSparkAction.java:83

  private final Identifier sourceTableIdent;

  // Optional Parameters for destination
  private final Map<String, String> additionalProperties = Maps.newHashMap();

  BaseTableCreationSparkAction(
      SparkSession spark, CatalogPlugin sourceCatalog, Identifier sourceTableIdent) {
    super(spark);

    this.sourceCatalog = checkSourceCatalog(sourceCatalog);
    this.sourceTableIdent = sourceTableIdent;

    try {
      this.sourceTable = (V1Table) this.sourceCatalog.loadTable(sourceTableIdent);
      this.sourceCatalogTable = sourceTable.v1Table();
    } catch (org.apache.spark.sql.catalyst.analysis.NoSuchTableException e) {
      throw new NoSuchTableException("Cannot find source table '%s'", sourceTableIdent);
    } catch (ClassCastException e) {
      throw new IllegalArgumentException(
          String.format("Cannot use non-v1 table '%s' as a source", sourceTableIdent), e);
    }
    validateSourceTable();

    this.sourceTableLocation =
        CatalogUtils.URIToString(sourceCatalogTable.storage().locationUri().get());
  }

  protected abstract TableCatalog checkSourceCatalog(CatalogPlugin catalog);

  protected abstract StagingTableCatalog destCatalog();

  protected abstract Identifier destTableIdent();

  protected abstract Map<String, String> destTableProps();

  protected String sourceTableLocation() {
    return sourceTableLocation;

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Use a source table registered in a V1 catalog (e.g. Hive/metastore tables via spark_catalog)
  2. For Iceberg-to-Iceberg copies use DataFrame read/write or a CTAS/RTAS statement instead of migrate/snapshot
  3. Point the source identifier at spark_catalog rather than a v2 session catalog

Example fix

// before
SparkActions.get(spark).migrate("iceberg_catalog", "db.tbl").execute(); // v2 source
// after
SparkActions.get(spark).migrate("spark_catalog", "db.parquet_tbl").execute();
Defensive patterns

Strategy: validation

Validate before calling

CatalogPlugin plugin = spark.sessionState().catalogManager().catalog(catalogName);
// migrate/snapshot sources must resolve to V1Table; check the provider first
String provider = spark.conf().get("spark.sql.catalog." + catalogName, "");

Type guard

boolean isV1Source(CatalogManager cm, String catalog) { return cm.catalog(catalog).loadTable(ident) instanceof V1Table; }

Try / catch

try { action.execute(); } catch (IllegalArgumentException e) { if (e.getMessage().contains("non-v1 table")) { /* use spark_catalog or a copy strategy */ } throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/48c0f1ac9a87d651. Report an issue: GitHub.