apache/iceberg · error · java.lang.IllegalArgumentException
Unknown Spark table type: %s
Error message
Unknown Spark table type: %s
What it means
SparkCatalog.loadTable(ident, version) handles a closed set of SparkTable subtypes (regular SparkTable, SparkChangelogTable). If the loaded table object is neither, the method throws IllegalArgumentException 'Unknown Spark table type: <class name>'. This indicates the table implementation was swapped by an extension or wrapper the catalog does not recognize.
Source
Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/SparkCatalog.java:214
} catch (NumberFormatException e) {
SnapshotRef ref = sparkTable.table().refs().get(version);
ValidationException.check(
ref != null,
"Cannot find matching snapshot ID or reference name for version %s",
version);
if (ref.isBranch()) {
return sparkTable.copyWithBranch(version);
} else {
return sparkTable.copyWithSnapshotId(ref.snapshotId());
}
}
} else if (table instanceof SparkChangelogTable) {
throw new UnsupportedOperationException("AS OF is not supported for changelogs");
} else {
throw new IllegalArgumentException("Unknown Spark table type: " + table.getClass().getName());
}
}
@Override
public Table loadTable(Identifier ident, long timestamp) throws NoSuchTableException {
Table table = loadTable(ident);
if (table instanceof SparkTable) {
SparkTable sparkTable = (SparkTable) table;
Preconditions.checkArgument(
sparkTable.snapshotId() == null && sparkTable.branch() == null,
"Cannot do time-travel based on both table identifier and AS OF");
// convert the timestamp to milliseconds as Spark passes microseconds
// but Iceberg uses milliseconds for snapshot timestamps
long timestampMillis = TimeUnit.MICROSECONDS.toMillis(timestamp);
long snapshotId = SnapshotUtil.snapshotIdAsOfTime(sparkTable.table(), timestampMillis);View on GitHub (pinned to 86d9c8fc54)
Solutions
- Ensure the catalog returns vanilla org.apache.iceberg.spark.SparkTable for time-travel loads; unwrap or register support for the custom type.
- Align Iceberg jar versions across the cluster (no mixed iceberg-spark-runtime versions on the classpath).
- Update the Iceberg extension/plugin to a version compatible with this SparkCatalog implementation.
- As a workaround, do time travel via DataFrame reader options (snapshot-id / as-of-timestamp) instead of the versioned loadTable API.
Example fix
// before
Table t = sparkCatalog.loadTable(ident, version); // custom wrapper type -> IllegalArgumentException
// after
Dataset<Row> df = spark.read().format("iceberg")
.option("snapshot-id", version)
.load("db.tbl"); // bypasses type dispatch Defensive patterns
Strategy: try-catch
Validate before calling
Table t = sparkCatalog.loadTable(ident);
boolean supported = t instanceof org.apache.iceberg.spark.SparkTable
|| t instanceof org.apache.iceberg.spark.SparkChangelogTable;
if (!supported) {
throw new IllegalStateException("Unsupported table implementation for time travel: " + t.getClass().getName());
} Type guard
boolean isVersionedLoadSupported = t instanceof org.apache.iceberg.spark.SparkTable;
Try / catch
try {
return sparkCatalog.loadTable(ident, version);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Unknown Spark table type")) { /* fall back to snapshot-id read option */ }
throw e;
} Prevention
- Pin a single iceberg-spark-runtime version on the classpath; avoid shaded duplicates.
- Verify extension/plugin catalogs return vanilla SparkTable instances.
- Prefer the DataFrame reader snapshot-id option for third-party table wrappers.
- Test time travel against every custom catalog in your integration suite.
When it happens
Trigger: loadTable(ident, version) where the resolved Table is a custom/extension subclass of SparkTable (e.g. from a third-party catalog plugin or instrumentation wrapper) that is not SparkTable or SparkChangelogTable.
Common situations: Using Iceberg extensions or vendor plugins that wrap SparkTable; classpath mixing of different Iceberg versions producing unexpected table classes; custom catalog returning its own Table implementation through a path that reaches SparkCatalog's time-travel loader.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- AS OF is not supported for changelogs
- Expected one (and same) ORC type for list elements, got:
- AS OF is not supported for changelogs
- Unknown Spark table type:
- Unsupported changelog scan task type: + task.getClass().getN
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/eb9bbbf7f2efd024.
Report an issue: GitHub.