apache/iceberg · error · IllegalArgumentException

Cannot use catalog ( ): not a TableCatalog

Error message

Cannot use catalog %s(%s): not a TableCatalog

What it means

Spark3Util.asTableCatalog asserts that a resolved Spark CatalogPlugin actually implements Spark's TableCatalog interface. Catalogs that only provide views or are metadata-only (e.g. a catalog that is not a table catalog) cannot load/modify tables, so this throws IllegalArgumentException with the catalog's name and class.

Solutions

  1. Point the operation at a catalog configured with a TableCatalog implementation (e.g. org.apache.iceberg.spark.SparkCatalog)
  2. Check the catalog class with SHOW CATALOGS / spark.conf.get("spark.sql.catalog.<name>") and fix the configuration
  3. Use the correct catalog prefix in the table identifier instead of relying on delegation

Example fix

// before
spark.conf.set("spark.sql.catalog.local", "org.apache.spark.sql.catalog.SomeNonTableCatalog");
// after
spark.conf.set("spark.sql.catalog.local", "org.apache.iceberg.spark.SparkCatalog");
Defensive patterns

Strategy: validation

Validate before calling

CatalogPlugin c = ...; if (!(c instanceof TableCatalog)) { throw new IllegalArgumentException("Catalog " + c.name() + " does not support tables"); }

Type guard

java.util.Optional<TableCatalog> asTableCatalog(CatalogPlugin c) { return (c instanceof TableCatalog) ? java.util.Optional.of((TableCatalog) c) : java.util.Optional.empty(); }

Try / catch

try { return catalog.loadTable(ident); } catch (IllegalArgumentException e) { if (e.getMessage().contains("not a TableCatalog")) { throw new IllegalStateException("Configure spark.sql.catalog.<name> to a TableCatalog"); } throw e; }

Prevention

When it happens

Trigger: Calling SparkCatalog/SparkSessionCatalog operations that funnel through asTableCatalog (loadTable, createTable, alterTable, etc.) when the resolved catalog is a CatalogPlugin but not a TableCatalog instance.

Common situations: Configuring spark.sql.catalog.<name> to a non-table catalog implementation and then addressing tables through it; delegating to the session catalog when it doesn't support tables; typos routing operations to the wrong 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


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

Appendix: source

Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java:879

              try {
                return catalogManager.catalog(catalogName);
              } catch (Exception e) {
                LOG.warn("Failed to load catalog: {}", catalogName, 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)