apache/iceberg · error · UnsupportedOperationException

does not support listing tables

Error message

 does not support listing tables

What it means

SparkCachedTableCatalog is a thin in-memory passthrough that only serves tables already cached in SparkTableCache; it has no backing metadata store, so listing namespaces/tables is meaningless and always throws UnsupportedOperationException.

Solutions

  1. Do not run SHOW TABLES against the cached-table catalog; it is not a discoverable catalog
  2. List tables on the underlying Iceberg catalog (e.g. the original session catalog) instead
  3. Reference cached tables directly by the exact cached identifier used when the table was cached
Defensive patterns

Strategy: try-catch

Validate before calling

if (catalog instanceof SparkCachedTableCatalog) { /* skip listing; use underlying catalog */ }

Try / catch

try { return catalog.listTables(namespace); } catch (UnsupportedOperationException e) { return underlyingCatalog.listTables(namespace); }

Prevention

When it happens

Trigger: Executing SHOW TABLES IN <cached-catalog-name> or any Spark catalog query invoking listTables(namespace) on a catalog initialized as org.apache.iceberg.spark.SparkCachedTableCatalog.

Common situations: Users pointing a session catalog at the cached-table catalog by mistake, or running SHOW TABLES/SHOW NAMESPACES against it expecting real catalog behavior.

Related errors


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

Appendix: source

Thrown at spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/SparkCachedTableCatalog.java:62

/** An internal table catalog that is capable of loading tables from a cache. */
public class SparkCachedTableCatalog implements TableCatalog, SupportsFunctions {

  private static final String CLASS_NAME = SparkCachedTableCatalog.class.getName();
  private static final Splitter COMMA = Splitter.on(",");
  private static final Pattern AT_TIMESTAMP = Pattern.compile("at_timestamp_(\\d+)");
  private static final Pattern SNAPSHOT_ID = Pattern.compile("snapshot_id_(\\d+)");
  private static final Pattern BRANCH = Pattern.compile("branch_(.*)");
  private static final Pattern TAG = Pattern.compile("tag_(.*)");
  private static final String REWRITE = "rewrite";

  private static final SparkTableCache TABLE_CACHE = SparkTableCache.get();

  private String name = null;

  @Override
  public Identifier[] listTables(String[] namespace) {
    throw new UnsupportedOperationException(CLASS_NAME + " does not support listing tables");
  }

  @Override
  public SparkTable loadTable(Identifier ident) throws NoSuchTableException {
    return load(ident);
  }

  @Override
  public SparkTable loadTable(Identifier ident, String version) throws NoSuchTableException {
    SparkTable table = load(ident);
    Preconditions.checkArgument(
        table.snapshotId() == null, "Cannot time travel based on both table identifier and AS OF");
    return table.copyWithSnapshotId(Long.parseLong(version));
  }

  @Override
  public SparkTable loadTable(Identifier ident, long timestampMicros) throws NoSuchTableException {
    SparkTable table = load(ident);

View on GitHub (pinned to 86d9c8fc54)