{"record":{"id":"32083a62f69f6ec8","repo":"apache/iceberg","slug":"sparkcachedtablecatalog-does-not-support-altering","errorCode":null,"errorMessage":"SparkCachedTableCatalog does not support altering tables","messagePattern":"SparkCachedTableCatalog does not support altering tables","errorType":"exception","errorClass":"java.lang.UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/SparkCachedTableCatalog.java","lineNumber":103,"sourceCode":"    long snapshotId = SnapshotUtil.snapshotIdAsOfTime(table.table(), timestampMillis);\n    return table.copyWithSnapshotId(snapshotId);\n  }\n\n  @Override\n  public void invalidateTable(Identifier ident) {\n    throw new UnsupportedOperationException(CLASS_NAME + \" does not support table invalidation\");\n  }\n\n  @Override\n  public SparkTable createTable(\n      Identifier ident, StructType schema, Transform[] partitions, Map<String, String> properties)\n      throws TableAlreadyExistsException {\n    throw new UnsupportedOperationException(CLASS_NAME + \" does not support creating tables\");\n  }\n\n  @Override\n  public SparkTable alterTable(Identifier ident, TableChange... changes) {\n    throw new UnsupportedOperationException(CLASS_NAME + \" does not support altering tables\");\n  }\n\n  @Override\n  public boolean dropTable(Identifier ident) {\n    throw new UnsupportedOperationException(CLASS_NAME + \" does not support dropping tables\");\n  }\n\n  @Override\n  public boolean purgeTable(Identifier ident) throws UnsupportedOperationException {\n    throw new UnsupportedOperationException(CLASS_NAME + \" does not support purging tables\");\n  }\n\n  @Override\n  public void renameTable(Identifier oldIdent, Identifier newIdent) {\n    throw new UnsupportedOperationException(CLASS_NAME + \" does not support renaming tables\");\n  }\n\n  @Override","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/SparkCachedTableCatalog.java#L85-L121","documentation":"SparkCachedTableCatalog is a read-only catalog that serves Iceberg tables from an in-process cache (e.g. for cached table scans in Spark). It intentionally implements the Spark CatalogPlugin DDL surface but rejects every mutation. alterTable() throws UnsupportedOperationException because cached tables are snapshots of existing tables and must be altered through the real catalog that owns them.","triggerScenarios":"Calling SparkCachedTableCatalog.alterTable(ident, changes...) directly, or running Spark SQL DDL (ALTER TABLE ... / DESCRIBE-driven ALTER) whose catalog resolution lands on the cached-table catalog (identifiers like 'spark_catalog.<cached-namespace>.<table>').","commonSituations":"Pasting an ALTER TABLE statement into a session where the table was loaded through the cached catalog; programmatically applying TableChange objects against the cache-backed catalog; framework code that generically applies schema evolution to whatever catalog the identifier resolves to.","solutions":["Resolve the table through the real catalog (SparkCatalog / HiveCatalog / HadoopCatalog) and apply the TableChange there.","Use ALTER TABLE on the original unqualified or properly catalog-qualified identifier so Spark does not route to SparkCachedTableCatalog.","If you own the code path, hold the underlying org.apache.iceberg.Table and use table.updateSchema()/updateProperties() instead of the catalog DDL API."],"exampleFix":"// before\nSparkCachedTableCatalog catalog = ...;\ncatalog.alterTable(Identifier.of(new String[]{\"db\"}, \"t\"), TableChange.addColumn(\"c\", Types.IntegerType.get()));\n\n// after\nSparkCatalog realCatalog = ...; // catalog that actually owns the table\nrealCatalog.alterTable(Identifier.of(new String[]{\"db\"}, \"t\"), TableChange.addColumn(\"c\", Types.IntegerType.get()));","handlingStrategy":"validation","validationCode":"if (catalog instanceof SparkCachedTableCatalog || catalog.name().contains(\"CachedTable\")) {\n  throw new IllegalArgumentException(\"DDL not supported on cached table catalog; use the owning catalog\");\n}","typeGuard":"boolean supportsDdl = !(catalog instanceof SparkCachedTableCatalog);","tryCatchPattern":null,"preventionTips":["Treat SparkCachedTableCatalog as read-only by design; route all ALTER/DROP/PURGE/RENAME to the owning catalog.","Use fully qualified identifiers (spark_catalog.db.t) in SQL so DDL resolves to the real catalog.","In generic tooling, feature-detect catalog capabilities before invoking DDL methods."],"tags":["spark","catalog","ddl","unsupported-operation","cached-table"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}