{"record":{"id":"380cc02696d7ee26","repo":"apache/iceberg","slug":"cannot-use-catalog-s-s-not-a-tablecatalog-380cc0","errorCode":null,"errorMessage":"Cannot use catalog %s(%s): not a TableCatalog","messagePattern":"Cannot use catalog (.+?)\\((.+?)\\): not a TableCatalog","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java","lineNumber":879,"sourceCode":"              try {\n                return catalogManager.catalog(catalogName);\n              } catch (Exception e) {\n                LOG.warn(\"Failed to load catalog: {}\", catalogName, e);\n                return null;\n              }\n            },\n            Identifier::of,\n            defaultCatalog,\n            currentNamespace);\n    return new CatalogAndIdentifier(catalogIdentifier);\n  }\n\n  private static TableCatalog asTableCatalog(CatalogPlugin catalog) {\n    if (catalog instanceof TableCatalog) {\n      return (TableCatalog) catalog;\n    }\n\n    throw new IllegalArgumentException(\n        String.format(\n            \"Cannot use catalog %s(%s): not a TableCatalog\",\n            catalog.name(), catalog.getClass().getName()));\n  }\n\n  /** This mimics a class inside of Spark which is private inside of LookupCatalog. */\n  public static class CatalogAndIdentifier {\n    private final CatalogPlugin catalog;\n    private final Identifier identifier;\n\n    public CatalogAndIdentifier(CatalogPlugin catalog, Identifier identifier) {\n      this.catalog = catalog;\n      this.identifier = identifier;\n    }\n\n    public CatalogAndIdentifier(Pair<CatalogPlugin, Identifier> identifier) {\n      this.catalog = identifier.first();\n      this.identifier = identifier.second();","sourceCodeStart":861,"sourceCodeEnd":897,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/Spark3Util.java#L861-L897","documentation":"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.","triggerScenarios":"Calling SparkCatalog/SparkSessionCatalog operations that funnel through asTableCatalog (loadTable, createTable, alterTable, etc.) when the resolved catalog is a CatalogPlugin but not a TableCatalog instance.","commonSituations":"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.","solutions":["Point the operation at a catalog configured with a TableCatalog implementation (e.g. org.apache.iceberg.spark.SparkCatalog)","Check the catalog class with SHOW CATALOGS / spark.conf.get(\"spark.sql.catalog.<name>\") and fix the configuration","Use the correct catalog prefix in the table identifier instead of relying on delegation"],"exampleFix":"// before\nspark.conf.set(\"spark.sql.catalog.local\", \"org.apache.spark.sql.catalog.SomeNonTableCatalog\");\n// after\nspark.conf.set(\"spark.sql.catalog.local\", \"org.apache.iceberg.spark.SparkCatalog\");","handlingStrategy":"validation","validationCode":"CatalogPlugin c = ...; if (!(c instanceof TableCatalog)) { throw new IllegalArgumentException(\"Catalog \" + c.name() + \" does not support tables\"); }","typeGuard":"java.util.Optional<TableCatalog> asTableCatalog(CatalogPlugin c) { return (c instanceof TableCatalog) ? java.util.Optional.of((TableCatalog) c) : java.util.Optional.empty(); }","tryCatchPattern":"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; }","preventionTips":["Verify every spark.sql.catalog.<name> config points to a TableCatalog implementation","Fully qualify table names so operations don't fall through to a non-table catalog","Add a startup check that resolves and asserts catalog types"],"tags":["spark","catalog","type-mismatch"],"backgroundTag":"invalid-argument-value","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}