apache/iceberg · error · java.lang.IllegalArgumentException

Cannot use non-v1 table

Error message

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

What it means

Spark table migration/copy actions (e.g. SNAPSHOTTABLE/MIGRATE) load the source table via a Spark catalog and cast it to V1Table. If the loaded table implementation is not a V1Table (e.g. a V2 catalog table), the ClassCastException is caught and rethrown as this IllegalArgumentException. Only Spark's legacy v1 session-catalog tables can be used as a migration source.

Solutions

  1. Use the default session catalog (spark_catalog) for the source table, migrating from Hive/Parquet/CSV sources
  2. Verify which catalog the identifier resolves to; qualify it explicitly (e.g. spark_catalog.db.tbl)
  3. If the source is already Iceberg, use a different workflow (e.g. registerTable or catalog migration) instead of migrate/snapshot

Example fix

// before
CALL iceberg.system.migrate('my_catalog.my_db.my_table')
// after
CALL iceberg.system.migrate('my_db.my_hive_table')  -- resolved via spark_catalog (V1Table)
Defensive patterns

Strategy: validation

Validate before calling

// Verify the source resolves to a V1Table before running the procedure
var catalog = spark.sessionState().catalogManager().catalog("spark_catalog");
var table = catalog.loadTable(Identifier.of(new String[]{"db"}, "tbl"));
if (!(table instanceof org.apache.spark.sql.execution.datasources.v2.V1Table)) {
  throw new IllegalStateException("Source is not a V1 (session catalog) table");
}

Type guard

function isV1Source(table) { return table instanceof org.apache.spark.sql.execution.datasources.v2.V1Table; }

Prevention

When it happens

Trigger: Calling a BaseTableCreationSparkAction subclass (MigrateTableProcedures/SnapshotTableProcedures) with a source table resolved by a catalog whose loadTable returns a Spark V2 Table implementation rather than org.apache.spark.sql.execution.datasources.v2.V1Table.

Common situations: Pointing the procedure at a table in an Iceberg or other V2 catalog instead of the built-in spark_catalog; the table identifier resolves through spark.sql.catalog.<name> whose table class is not V1Table.

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

Appendix: source

Thrown at spark/v4.2/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)