{"record":{"id":"e90862d28e0419bf","repo":"apache/iceberg","slug":"cannot-find-snapshot-after-s-not-an-ancestor-of","errorCode":null,"errorMessage":"Cannot find snapshot after %s: not an ancestor of table's current snapshot","messagePattern":"Cannot find snapshot after (.+?): not an ancestor of table's current snapshot","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/util/SnapshotUtil.java","lineNumber":299,"sourceCode":"  /**\n   * Traverses the history of the table's current snapshot and finds the snapshot with the given\n   * snapshot id as its parent.\n   *\n   * @return the snapshot for which the given snapshot is the parent\n   * @throws IllegalArgumentException when the given snapshotId is not found in the table\n   * @throws IllegalStateException when the given snapshotId is not an ancestor of the current table\n   *     state\n   */\n  public static Snapshot snapshotAfter(Table table, long snapshotId) {\n    Preconditions.checkArgument(\n        table.snapshot(snapshotId) != null, \"Cannot find parent snapshot: %s\", snapshotId);\n    for (Snapshot current : currentAncestors(table)) {\n      if (current.parentId() == snapshotId) {\n        return current;\n      }\n    }\n\n    throw new IllegalStateException(\n        String.format(\n            \"Cannot find snapshot after %s: not an ancestor of table's current snapshot\",\n            snapshotId));\n  }\n\n  /**\n   * Returns the ID of the most recent snapshot for the table as of the timestamp.\n   *\n   * @param table a {@link Table}\n   * @param timestampMillis the timestamp in millis since the Unix epoch\n   * @return the snapshot ID\n   * @throws IllegalArgumentException when no snapshot is found in the table older than the\n   *     timestamp\n   */\n  public static long snapshotIdAsOfTime(Table table, long timestampMillis) {\n    Long snapshotId = nullableSnapshotIdAsOfTime(table, timestampMillis);\n\n    Preconditions.checkArgument(","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/util/SnapshotUtil.java#L281-L317","documentation":"SnapshotUtil.snapshotAfter returns the snapshot that is the direct child of the given snapshotId along the current table ancestry. If snapshotId is not an ancestor of the table's current snapshot (so no child of it exists in the current lineage), it throws this IllegalStateException with the offending id.","triggerScenarios":"Calling SnapshotUtil.snapshotAfter(table, snapshotId) with an id from a different branch (e.g. a rolled-back or WAP branch), an expired snapshot id, or an id from another table entirely.","commonSituations":"Incremental consumers bookmarking a snapshot id that was later rolled back (rollbackToSnapshot rewrote the lineage); snapshot expiry removed the ancestor; confusing cherry-picked/staged snapshot ids with mainline ancestor ids.","solutions":["Check SnapshotUtil.isAncestorOf(table, snapshotId) before calling snapshotAfter","Refresh the table and confirm the bookmarked snapshot id is still in current lineage; if not, rescan from the current snapshot","Catch IllegalStateException and reset the consumer's bookmark to the current snapshot","Avoid bookmarking staged/cherry-pick snapshot ids; only persist ids from the published main lineage"],"exampleFix":"// before\nSnapshot next = SnapshotUtil.snapshotAfter(table, bookmarkId);\n// after\nif (!SnapshotUtil.isAncestorOf(table, bookmarkId)) {\n  bookmarkId = table.currentSnapshot().snapshotId(); // reset bookmark\n}\nSnapshot next = SnapshotUtil.snapshotAfter(table, bookmarkId);","handlingStrategy":"validation","validationCode":"boolean inLineage = SnapshotUtil.isAncestorOf(table, snapshotId);\nif (!inLineage) {\n  snapshotId = table.currentSnapshot().snapshotId(); // reset bookmark\n}","typeGuard":null,"tryCatchPattern":"try {\n  Snapshot next = SnapshotUtil.snapshotAfter(table, bookmarkId);\n} catch (IllegalStateException e) {\n  bookmarkId = table.refresh().currentSnapshot().snapshotId();\n  next = null; // rescan\n}","preventionTips":["Verify isAncestorOf before bookmark advancement","Handle rollback/expiry invalidating bookmarks in consumers","Only persist ids from the main published lineage","Refresh table metadata before lineage checks"],"tags":["snapshots","incremental-consumption"],"backgroundTag":"resource-not-found","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"}