apache/iceberg · error · java.lang.IllegalArgumentException
Cannot use catalog %s(%s): not a TableCatalog
Error message
Cannot use catalog %s(%s): not a TableCatalog
What it means
asTableCatalog casts a Spark CatalogPlugin to TableCatalog and throws IllegalArgumentException when the catalog implementation does not support table operations (e.g. it is a function catalog or a generic catalog).
Source
Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java:827
catalogName -> {
try {
return catalogManager.catalog(catalogName);
} catch (Exception e) {
return null;
}
},
Identifier::of,
defaultCatalog,
currentNamespace);
return new CatalogAndIdentifier(catalogIdentifier);
}
private static TableCatalog asTableCatalog(CatalogPlugin catalog) {
if (catalog instanceof TableCatalog) {
return (TableCatalog) catalog;
}
throw new IllegalArgumentException(
String.format(
"Cannot use catalog %s(%s): not a TableCatalog",
catalog.name(), catalog.getClass().getName()));
}
/** This mimics a class inside of Spark which is private inside of LookupCatalog. */
public static class CatalogAndIdentifier {
private final CatalogPlugin catalog;
private final Identifier identifier;
public CatalogAndIdentifier(CatalogPlugin catalog, Identifier identifier) {
this.catalog = catalog;
this.identifier = identifier;
}
public CatalogAndIdentifier(Pair<CatalogPlugin, Identifier> identifier) {
this.catalog = identifier.first();
this.identifier = identifier.second();View on GitHub (pinned to 86d9c8fc54)
Solutions
- Set spark.sql.catalog.<name> to a TableCatalog implementation (e.g. org.apache.iceberg.spark.SparkCatalog or SparkSessionCatalog)
- Verify the catalog class with: spark.sessionState().catalogManager().catalog(name) instanceof TableCatalog before use
- Use the correct catalog name in the identifier — the current name resolves to the wrong plugin
Example fix
// before
spark.conf.set("spark.sql.catalog.my_cat", "org.apache.spark.sql.catalog.MyFuncCatalog");
// after
spark.conf.set("spark.sql.catalog.my_cat", "org.apache.iceberg.spark.SparkCatalog"); Defensive patterns
Strategy: type-guard
Validate before calling
CatalogPlugin cat = catalogManager.catalog(name);
if (!(cat instanceof TableCatalog)) { throw new IllegalStateException(name + " is not a TableCatalog"); } Type guard
if (catalog instanceof TableCatalog) { /* safe to use */ } Try / catch
try { return Spark3Util.catalogAndIdentifier(spark, ident); } catch (IllegalArgumentException e) { if (e.getMessage().contains("not a TableCatalog")) { /* fix catalog config */ } throw e; } Prevention
- Register catalogs with org.apache.iceberg.spark.SparkCatalog or SparkSessionCatalog
- Check catalog instanceof TableCatalog before DDL/identifier resolution
- Avoid using function-only catalogs as default catalog for table operations
When it happens
Trigger: Resolving a table identifier whose first part names a catalog that is not a TableCatalog — e.g. pointing Iceberg procedures/ALTER TABLE at a catalog registered as CatalogPlugin but not implementing TableCatalog.
Common situations: Misconfigured spark.sql.catalog.<name> entries where the class implements only CatalogPlugin; using session catalog defaults that resolve to a non-table catalog.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid session catalog: sparkSessionCatalog
- Invalid session catalog: ${sparkSessionCatalog}
- Invalid session catalog:
- Failed to load catalog: {}
- Failed to load catalog: {}
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/bd8145e6ebb88d6e.
Report an issue: GitHub.