{"record":{"id":"f56c0d501a8910f6","repo":"apache/iceberg","slug":"as-of-is-not-supported-for-changelogs-f56c0d","errorCode":null,"errorMessage":"AS OF is not supported for changelogs","messagePattern":"AS OF is not supported for changelogs","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/SparkCatalog.java","lineNumber":212,"sourceCode":"\n      try {\n        return sparkTable.copyWithSnapshotId(Long.parseLong(version));\n      } catch (NumberFormatException e) {\n        SnapshotRef ref = sparkTable.table().refs().get(version);\n        ValidationException.check(\n            ref != null,\n            \"Cannot find matching snapshot ID or reference name for version %s\",\n            version);\n\n        if (ref.isBranch()) {\n          return sparkTable.copyWithBranch(version);\n        } else {\n          return sparkTable.copyWithSnapshotId(ref.snapshotId());\n        }\n      }\n\n    } else if (table instanceof SparkChangelogTable) {\n      throw new UnsupportedOperationException(\"AS OF is not supported for changelogs\");\n\n    } else {\n      throw new IllegalArgumentException(\"Unknown Spark table type: \" + table.getClass().getName());\n    }\n  }\n\n  @Override\n  public Table loadTable(Identifier ident, long timestamp) throws NoSuchTableException {\n    Table table = loadTable(ident);\n\n    if (table instanceof SparkTable) {\n      SparkTable sparkTable = (SparkTable) table;\n\n      Preconditions.checkArgument(\n          sparkTable.snapshotId() == null && sparkTable.branch() == null,\n          \"Cannot do time-travel based on both table identifier and AS OF\");\n\n      // convert the timestamp to milliseconds as Spark passes microseconds","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/SparkCatalog.java#L194-L230","documentation":"Iceberg's SparkCatalog cannot perform time-travel (AS OF / VERSION AS OF) reads on a SparkChangelogTable. Changelog tables expose a stream of changes rather than snapshot-addressable state, so there is no snapshot ID to resolve a version to. The catalog deliberately rejects this with UnsupportedOperationException.","triggerScenarios":"Calling Spark's time-travel syntax on a changelog view of a table, e.g. SELECT * FROM catalog.db.changelog_table VERSION AS OF 12345 (loadTable(ident, version) where the loaded table is a SparkChangelogTable).","commonSituations":"Developers reading change-data-capture / changelog procedures (e.g. CALL catalog.system.changelog...) and then adding AS OF or FOR VERSION AS OF clauses as they would on a normal Iceberg table.","solutions":["Remove the VERSION AS OF clause and read the changelog table without time travel.","If historical state is needed, time-travel on the base table (catalog.db.table VERSION AS OF x) instead of its changelog view.","Use snapshot reads on the base table plus explicit change computation instead of the changelog procedure.","Upgrade Iceberg only if a newer release adds changelog time-travel support (currently unsupported by design)."],"exampleFix":"// before\nspark.sql(\"SELECT * FROM prod.db.tbl_changes VERSION AS OF 3821559239432519331\");\n// after\nspark.sql(\"SELECT * FROM prod.db.tbl_changes\"); // no AS OF on changelog tables","handlingStrategy":"validation","validationCode":"String sql = stmt.sqlText();\nif (sql.toUpperCase(Locale.ROOT).contains(\"VERSION AS OF\") && isChangelogTable(stmt)) {\n  throw new IllegalArgumentException(\"AS OF is not supported for changelog tables\");\n}","typeGuard":"boolean isChangelogTable(org.apache.spark.sql.connector.catalog.Table t) {\n  return t instanceof org.apache.iceberg.spark.SparkChangelogTable;\n}","tryCatchPattern":"try {\n  return catalog.loadTable(ident, version);\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"AS OF is not supported\")) {\n    return catalog.loadTable(ident); // fall back to current state\n  }\n  throw e;\n}","preventionTips":["Never combine time-travel clauses with changelog/procedure-produced tables.","Check the table type (SparkChangelogTable) before adding AS OF clauses programmatically.","Document in pipeline code that changelog reads are forward-only.","Keep time-travel queries on the base table, not the change feed."],"tags":["spark","iceberg","time-travel","changelog"],"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"}