{"record":{"id":"5d5556a1b5d89046","repo":"apache/iceberg","slug":"does-not-support-table-invalidation","errorCode":null,"errorMessage":" does not support table invalidation","messagePattern":" does not support table invalidation","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/SparkCachedTableCatalog.java","lineNumber":91,"sourceCode":"    Preconditions.checkArgument(\n        table.snapshotId() == null, \"Cannot time travel based on both table identifier and AS OF\");\n    return table.copyWithSnapshotId(Long.parseLong(version));\n  }\n\n  @Override\n  public SparkTable loadTable(Identifier ident, long timestampMicros) throws NoSuchTableException {\n    SparkTable table = load(ident);\n    Preconditions.checkArgument(\n        table.snapshotId() == null, \"Cannot time travel based on both table identifier and AS OF\");\n    // Spark passes microseconds but Iceberg uses milliseconds for snapshots\n    long timestampMillis = TimeUnit.MICROSECONDS.toMillis(timestampMicros);\n    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  }","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/SparkCachedTableCatalog.java#L73-L109","documentation":"Capability guard in SparkCachedTableCatalog.invalidateTable (v4.0): cached table handles are looked up fresh from the underlying catalog each time, so there is nothing to invalidate and the operation is refused with the catalog class name in the message.","triggerScenarios":"Spark calling catalog.invalidateTable(ident) after writes/DDL, e.g. when executing INSERT/REFRESH flows against a table resolved via the cached-table catalog.","commonSituations":"Mixed-use SQL where Spark internally invalidates metadata caches between statements against tables bound to this catalog.","solutions":["Route table access through the regular Iceberg SparkCatalog, which supports invalidation","Remove the cached entry via SparkTableCache management rather than invalidating through this catalog","Avoid DDL/write workflows that trigger cache invalidation on tables exposed by this catalog"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { catalog.invalidateTable(ident); } catch (UnsupportedOperationException e) { /* rely on SparkTableCache lifecycle */ }","preventionTips":["Manage cache freshness through SparkTableCache, not Catalog#invalidateTable","Point write-heavy workloads at SparkCatalog instead of the cached passthrough"],"tags":["spark","catalog","cache","unsupported-operation"],"backgroundTag":"operation-not-supported","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"}